source-engine/hammer/texturewindow.cpp

938 lines
21 KiB
C++
Raw Normal View History

2020-04-22 16:56:21 +00:00
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=============================================================================
#include "stdafx.h"
#pragma warning(push, 1)
#pragma warning(disable:4701 4702 4530)
#include <fstream>
#pragma warning(pop)
#include "hammer.h"
#include "TextureWindow.h"
#include "TextureBrowser.h"
#include "CustomMessages.h"
#include "IEditorTexture.h"
#include "GameConfig.h"
#include "GlobalFunctions.h"
#include "TextureSystem.h"
#include "materialsystem/imaterial.h"
#include "materialsystem/imaterialsystem.h"
// memdbgon must be the last include file in a .cpp file!!!
#include <tier0/memdbgon.h>
const DWORD NO_FILE_FILTER = 0xFFFFFFF0L;
const int iPadding = 4;
const int iTexNameFontHeight = 7;
const int iTexIconHeight = 12;
BEGIN_MESSAGE_MAP(CTextureWindow, CWnd)
//{{AFX_MSG_MAP(CTextureWindow)
ON_WM_PAINT()
ON_WM_SIZE()
ON_WM_HSCROLL()
ON_WM_VSCROLL()
ON_WM_LBUTTONDOWN()
ON_WM_LBUTTONDBLCLK()
ON_WM_KEYDOWN()
ON_WM_MOUSEWHEEL()
ON_WM_CHAR()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
//-----------------------------------------------------------------------------
// Purpose: Constructor. Initializes data members.
//-----------------------------------------------------------------------------
CTextureWindow::CTextureWindow(void)
{
bFirstPaint = TRUE;
m_szFilter[0] = '\0';
m_nFilters = 0;
m_szKeywords[0] = '\0';
m_nKeywords = 0;
m_pSpecificList = NULL;
szCurTexture[0] = '\0';
m_eTextureFormat = g_pGameConfig->GetTextureFormat();
m_bEnableUpdate = true;
m_nTypeFilter = ~0;
m_bShowErrors = true;
}
//-----------------------------------------------------------------------------
// Purpose: Destructor.
//-----------------------------------------------------------------------------
CTextureWindow::~CTextureWindow(void)
{
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pParentWnd -
// rect -
//-----------------------------------------------------------------------------
void CTextureWindow::Create(CWnd *pParentWnd, RECT& rect)
{
static CString TextureWndClassName;
iDisplaySize = 64;
if(TextureWndClassName.IsEmpty())
{
// create class
TextureWndClassName = AfxRegisterWndClass(CS_DBLCLKS | CS_HREDRAW |
CS_VREDRAW, LoadCursor(NULL, IDC_ARROW),
(HBRUSH) GetStockObject(BLACK_BRUSH),
AfxGetApp()->LoadIcon(IDI_TEXTUREWINDOW));
}
CWnd::Create(TextureWndClassName, "TextureBrowserWindow",
SS_SUNKEN | WS_TABSTOP | WS_CHILD | WS_VSCROLL | WS_HSCROLL,
rect, pParentWnd, IDC_TEXTUREWINDOW);
UpdateScrollSizes();
// create font
if(!TexFont.m_hObject)
TexFont.CreatePointFont(iTexNameFontHeight * 10, "Courier New");
CDC *pDC = GetDC();
pDC->SelectObject(&TexFont);
pDC->GetCharWidth('A', 'A', &iTexNameCharWidth);
ReleaseDC(pDC);
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : bEnable -
//-----------------------------------------------------------------------------
void CTextureWindow::EnableUpdate(bool bEnable)
{
m_bEnableUpdate = bEnable;
}
//-----------------------------------------------------------------------------
// Purpose: Searches for all of the keywords in an array of keywords within
// a given search string, case-insensitive.
// Input : pszSearch - String to search for keywords within.
// pszKeyword - Array of pointers to keywords.
// nKeywords - Number of keywords in the array.
// Output : Returns true if all keywords were found, false otherwise.
//-----------------------------------------------------------------------------
bool CTextureWindow::MatchKeywords(const char *pszSearch, char **pszKeyword, int nKeywords)
{
if (nKeywords != 0)
{
for (int i = 0; i < nKeywords; i++)
{
if (Q_stristr(pszSearch, pszKeyword[i]) == NULL)
{
return(false);
}
}
}
return(true);
}
//-----------------------------------------------------------------------------
// Changes type filter bits
//-----------------------------------------------------------------------------
void CTextureWindow::SetTypeFilter( int filter, bool enable )
{
if (enable)
m_nTypeFilter |= filter;
else
m_nTypeFilter &= ~filter;
if (m_bEnableUpdate)
{
UpdateScrollSizes();
SelectTexture(szCurTexture, false);
if (IsWindow(m_hWnd))
{
Invalidate();
UpdateWindow();
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pTE -
// bStart -
// Output : Returns TRUE on success, FALSE on failure.
//-----------------------------------------------------------------------------
BOOL CTextureWindow::EnumTexturePositions(TWENUMPOS *pTE, BOOL bStart)
{
RECT &texrect = pTE->texrect;
if (bStart)
{
pTE->cur_x = iPadding;
pTE->cur_y = iPadding;
pTE->largest_y = 0;
pTE->iTexIndex = 0;
if (IsWindow(m_hWnd))
{
GetClientRect(&pTE->clientrect);
}
SetRect(&texrect, 0, 0, 0, 0);
}
bool bFound = false;
do
{
pTE->pTex = g_Textures.EnumActiveTextures(&pTE->iTexIndex, m_eTextureFormat);
if (pTE->pTex == NULL)
continue;
bFound = false;
// If we are iterating a specific list of textures, make sure it is in the list.
// dvs: inefficient, the specific list should control the loop, not act as a filter
if (m_pSpecificList != NULL)
{
int nIndex = m_pSpecificList->Find(pTE->pTex);
if (nIndex == -1)
continue;
pTE->nUsageCount = m_pSpecificList->Element(nIndex).nUsageCount;
}
// Filter by texture name.
char szTemp[MAX_PATH];
pTE->pTex->GetShortName(szTemp);
if (MatchKeywords(szTemp, m_Filters, m_nFilters))
{
//
// Filter by keywords.
//
// NOTE: Try not to access the material here when finding the position
// because it causes the materials to be cached (slow!!)
if (m_nKeywords)
{
pTE->pTex->GetKeywords(szTemp);
if (MatchKeywords(szTemp, m_Keyword, m_nKeywords))
{
bFound = true;
}
}
else
{
bFound = true;
}
}
// Filter based on opacity, etc.
// NOTE: Try not to access the material here when finding the position
// because it causes the materials to be cached (slow!!)
if (bFound && ((m_nTypeFilter & TYPEFILTER_ALL) != TYPEFILTER_ALL))
{
IMaterial* pMaterial = pTE->pTex->GetMaterial();
if (pMaterial)
{
bFound = false;
if ( pMaterial->GetMaterialVarFlag( MATERIAL_VAR_SELFILLUM ) )
{
if (m_nTypeFilter & TYPEFILTER_SELFILLUM)
bFound = true;
}
if ( pMaterial->GetMaterialVarFlag( MATERIAL_VAR_BASEALPHAENVMAPMASK ) )
{
if (m_nTypeFilter & TYPEFILTER_ENVMASK)
bFound = true;
}
if ( pMaterial->GetMaterialVarFlag( MATERIAL_VAR_TRANSLUCENT ) )
{
if (m_nTypeFilter & TYPEFILTER_TRANSLUCENT)
bFound = true;
}
else
{
if (m_nTypeFilter & TYPEFILTER_OPAQUE)
bFound = true;
}
}
}
// Blow off zero-size materials, but only if they've been loaded...
// Otherwise we have to cache everything which will take forever...
if ( bFound && pTE->pTex->IsLoaded() )
{
if ((pTE->pTex->GetWidth() == 0) || (pTE->pTex->GetHeight() == 0))
{
bFound = false;
}
}
} while ((pTE->pTex != NULL) && (!bFound));
if ((!bFound) || (pTE->pTex == NULL))
{
return(FALSE);
}
doresize:
SetRect( &texrect, pTE->cur_x, pTE->cur_y,
pTE->cur_x + iDisplaySize,
pTE->cur_y + iDisplaySize );
// if we've got one texture on this row already, and this one goes out of
// the client area, jump to the next row. we want to have at least one texture on
// each row, or we will sit in an infinite loop.
if(pTE->cur_x > iPadding && texrect.right > pTE->clientrect.right)
{
pTE->cur_x = iPadding;
pTE->cur_y = pTE->largest_y + iPadding;
goto doresize;
}
texrect.bottom += (8 + iTexNameFontHeight + iTexIconHeight);
if(texrect.bottom > pTE->largest_y)
pTE->largest_y = texrect.bottom;
// update cur_x
pTE->cur_x = texrect.right + iPadding;
return TRUE;
}
//-----------------------------------------------------------------------------
// Purpose: Sets the dimensions of each texture within the texture window.
// Input : iSize - 32 to display as 32 x 32 textures.
// 64 to display as 64 x 64 textures.
// 128 to display as 128 x 128 textures.
// 512 to display as 512 x 512 textures
//-----------------------------------------------------------------------------
void CTextureWindow::SetDisplaySize(int iSize)
{
iDisplaySize = iSize;
UpdateScrollSizes();
SelectTexture(szCurTexture, FALSE);
RedrawWindow();
}
//-----------------------------------------------------------------------------
// Purpose: Sets the name filter that is used to filter the texture window contents.
// Input : pszFilter - Space, comma, or semicolon delimited names to filter against.
//-----------------------------------------------------------------------------
void CTextureWindow::SetNameFilter(LPCTSTR pszFilter)
{
if (m_bEnableUpdate)
{
// kill highlight
HighlightCurTexture();
}
// set filter
strcpy(m_szFilter, pszFilter);
strupr(m_szFilter);
// delimit the filter
m_nFilters = 0;
char *p = strtok(m_szFilter, " ,;");
while (p != NULL)
{
m_Filters[m_nFilters++] = p;
p = strtok(NULL, " ,;");
}
if (m_bEnableUpdate)
{
UpdateScrollSizes();
SelectTexture(szCurTexture, false);
if (IsWindow(m_hWnd))
{
Invalidate();
UpdateWindow();
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Sets the keywords that are used to filter the texture window contents.
// Input : pszFilter - Space, comma, or semicolon delimited keywords to filter against.
//-----------------------------------------------------------------------------
void CTextureWindow::SetKeywords(LPCTSTR pszKeywords)
{
if (m_bEnableUpdate)
{
// kill highlight
HighlightCurTexture();
}
// set keyword filter
strcpy(m_szKeywords, pszKeywords);
strupr(m_szKeywords);
// delimit the filter
m_nKeywords = 0;
char *p = strtok(m_szKeywords, " ,;");
while (p != NULL)
{
m_Keyword[m_nKeywords++] = p;
p = strtok(NULL, " ,;");
}
if (m_bEnableUpdate)
{
UpdateScrollSizes();
SelectTexture(szCurTexture, false);
if (IsWindow(m_hWnd))
{
Invalidate();
UpdateWindow();
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTextureWindow::UpdateScrollSizes(void)
{
TWENUMPOS TE;
total_x = total_y = 0;
if(EnumTexturePositions(&TE, TRUE))
do {
if(TE.texrect.right > total_x)
total_x = TE.texrect.right;
if(TE.texrect.bottom > total_y)
total_y = TE.texrect.bottom;
} while(EnumTexturePositions(&TE));
// update total_x and total_y
total_x += iPadding;
total_y += iPadding;
SCROLLINFO si;
si.cbSize = sizeof(SCROLLINFO);
si.fMask = SIF_ALL;
si.nMin = 0;
si.nPos = 0;
si.nMax = total_x;
si.nPage = TE.clientrect.right;
SetScrollInfo(SB_HORZ, &si, TRUE);
si.nMax = total_y;
si.nPage = TE.clientrect.bottom;
SetScrollInfo(SB_VERT, &si, TRUE);
char szbuf[100];
sprintf(szbuf, "Size = %d %d\n", total_y, TE.clientrect.bottom);
TRACE0(szbuf);
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CTextureWindow::OnPaint(void)
{
CPaintDC dc(this); // device context for painting
// setup font
dc.SelectObject(&TexFont);
dc.SetTextColor(RGB(255, 255, 255));
//dc.SetBkColor(RGB(0,0,0));
dc.SetBkMode(TRANSPARENT);
CRect clientrect;
GetClientRect(clientrect);
dc.SetWindowOrg(GetScrollPos(SB_HORZ), GetScrollPos(SB_VERT));
TWENUMPOS TE;
BOOL bNotDone = EnumTexturePositions(&TE, TRUE);
BOOL bFoundHighlight = FALSE;
rectHighlight.left = -1;
BOOL bFirst = TRUE;
char szDrawTexture[128];
char szFirstDrawnTexture[128];
while (bNotDone)
{
TE.pTex->GetShortName(szDrawTexture);
if (!strcmpi(szCurTexture, szDrawTexture))
{
rectHighlight = TE.texrect;
rectHighlight.InflateRect(2, 4);
bFoundHighlight = TRUE;
}
if (dc.RectVisible(&TE.texrect))
{
// ensure loaded
TE.pTex->Load();
CPalette *pOld = dc.SelectPalette(TE.pTex->HasPalette() ? TE.pTex->GetPalette() : g_pGameConfig->Palette, FALSE);
dc.RealizePalette();
int flags = drawCaption | drawIcons;
if (m_bShowErrors)
flags |= drawErrors;
DrawTexData_t DrawTexData;
DrawTexData.nFlags = flags | (m_pSpecificList ? drawUsageCount : 0);
DrawTexData.nUsageCount = TE.nUsageCount;
TE.pTex->Draw(&dc, TE.texrect, iTexNameFontHeight, iTexIconHeight, DrawTexData);
dc.SelectPalette(pOld, FALSE);
}
//
// Save the name of the first drawn texture in case we need to highlight it.
//
if (bFirst)
{
bFirst = FALSE;
strcpy(szFirstDrawnTexture, szDrawTexture);
}
// next texture & position
bNotDone = EnumTexturePositions(&TE);
}
if(bFoundHighlight)
{
HighlightCurTexture(&dc);
}
else
{
// select first texture
SelectTexture(szFirstDrawnTexture);
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : nType -
// cx -
// cy -
//-----------------------------------------------------------------------------
void CTextureWindow::OnSize(UINT nType, int cx, int cy)
{
CWnd::OnSize(nType, cx, cy);
UpdateScrollSizes();
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : nSBCode -
// nPos -
// pScrollBar -
//-----------------------------------------------------------------------------
void CTextureWindow::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
int iPos = int(nPos);
SCROLLINFO si;
GetScrollInfo(SB_HORZ, &si);
int iCurPos = GetScrollPos(SB_HORZ);
int iLimit = GetScrollLimit(SB_HORZ);
switch(nSBCode)
{
case SB_LINELEFT:
iPos = -int(si.nPage / 4);
break;
case SB_LINERIGHT:
iPos = int(si.nPage / 4);
break;
case SB_PAGELEFT:
iPos = -int(si.nPage / 2);
break;
case SB_PAGERIGHT:
iPos = int(si.nPage / 2);
break;
case SB_THUMBTRACK:
case SB_THUMBPOSITION:
iPos -= iCurPos;
break;
}
if(iCurPos + iPos < 0)
iPos = -iCurPos;
if(iCurPos + iPos > iLimit)
iPos = iLimit - iCurPos;
if(iPos)
{
SetScrollPos(SB_HORZ, iCurPos + iPos);
ScrollWindow(-iPos, 0);
UpdateWindow();
}
CWnd::OnHScroll(nSBCode, nPos, pScrollBar);
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : nSBCode -
// nPos -
// pScrollBar -
//-----------------------------------------------------------------------------
void CTextureWindow::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
SCROLLINFO si;
GetScrollInfo(SB_VERT, &si);
int iCurPos = GetScrollPos(SB_VERT);
int iLimit = GetScrollLimit(SB_VERT);
int iPos = int(si.nPos);
switch(nSBCode)
{
case SB_LINEUP:
iPos = -int(si.nPage / 4);
break;
case SB_LINEDOWN:
iPos = int(si.nPage / 4);
break;
case SB_PAGEUP:
iPos = -int(si.nPage / 2);
break;
case SB_PAGEDOWN:
iPos = int(si.nPage / 2);
break;
case SB_THUMBTRACK:
case SB_THUMBPOSITION:
iPos = si.nTrackPos - iCurPos;
break;
case SB_ENDSCROLL:
iPos = 0;
break;
}
if(iCurPos + iPos < 0)
iPos = -iCurPos;
if(iCurPos + iPos > iLimit)
iPos = iLimit - iCurPos;
if(iPos)
{
SetScrollPos(SB_VERT, iCurPos + iPos);
ScrollWindow(0, -iPos);
UpdateWindow();
}
CWnd::OnVScroll(nSBCode, nPos, pScrollBar);
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *pDC -
//-----------------------------------------------------------------------------
void CTextureWindow::HighlightCurTexture(CDC *pDC)
{
CDC dc;
BOOL bMadeDC = FALSE;
// nothing to draw
if(rectHighlight.left < 0)
return;
if(!pDC)
{
dc.Attach(::GetDC(m_hWnd));
dc.SetWindowOrg(GetScrollPos(SB_HORZ), GetScrollPos(SB_VERT));
bMadeDC = TRUE;
pDC = &dc;
}
pDC->SelectStockObject(WHITE_PEN);
pDC->SelectStockObject(NULL_BRUSH);
pDC->SetROP2(R2_XORPEN);
pDC->Rectangle(rectHighlight);
if(bMadeDC)
::ReleaseDC(m_hWnd, dc.Detach());
}
//-----------------------------------------------------------------------------
// Purpose: Selects a given texture by name, scrolling the window if necessary
// to make the selected texture visible.
// Input : pszTexture - Texture to select.
//-----------------------------------------------------------------------------
void CTextureWindow::SelectTexture(LPCTSTR pszTexture, BOOL bAllowRedraw)
{
TWENUMPOS TE;
BOOL bNotDone = EnumTexturePositions(&TE, TRUE);
int iTextureIndex = 0;
IEditorTexture *pTex = g_Textures.FindActiveTexture(pszTexture);
CRect r(100, 100, 500, 500);
if (IsWindow(m_hWnd))
{
GetClientRect(r);
}
int iClientHeight = r.Height();
if (pTex == NULL)
{
return;
}
while (bNotDone)
{
if (pTex == TE.pTex)
{
// found it - make sure it's visible
if (IsWindow(m_hWnd))
{
int iScrollPos = GetScrollPos(SB_VERT);
if (iScrollPos + iClientHeight < TE.texrect.top || TE.texrect.bottom < iScrollPos)
{
SetScrollPos(SB_VERT, TE.texrect.top);
ScrollWindow(0, iScrollPos - TE.texrect.top);
if (bAllowRedraw)
{
RedrawWindow();
}
}
// first remove current highlight
HighlightCurTexture();
}
pTex->GetShortName(szCurTexture);
// highlight new texture
if (IsWindow(m_hWnd))
{
rectHighlight = CRect(&TE.texrect);
rectHighlight.InflateRect(2, 4);
HighlightCurTexture();
}
GetParent()->PostMessage(TWN_SELCHANGED);
return;
}
// next texture & position
bNotDone = EnumTexturePositions(&TE);
++iTextureIndex;
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : nFlags -
// point -
//-----------------------------------------------------------------------------
void CTextureWindow::OnLButtonDown(UINT nFlags, CPoint point)
{
// find clicked texture
TWENUMPOS TE;
BOOL bNotDone = EnumTexturePositions(&TE, TRUE);
int iTextureIndex = 0;
int iHorzPos = GetScrollPos(SB_HORZ);
int iVertPos = GetScrollPos(SB_VERT);
char szNewTexture[128];
point += CPoint(iHorzPos, iVertPos);
while (bNotDone)
{
if (PtInRect(&TE.texrect, point))
{
TE.pTex->GetShortName(szNewTexture);
break;
}
// next texture & position
bNotDone = EnumTexturePositions(&TE);
++iTextureIndex;
}
if(!bNotDone)
{
// no texture was hit
return;
}
// first remove current highlight
HighlightCurTexture();
// highlight new texture
strcpy(szCurTexture, szNewTexture);
rectHighlight = CRect(&TE.texrect);
rectHighlight.InflateRect(2, 4);
HighlightCurTexture();
// tell parent we changed selection
GetParent()->PostMessage(TWN_SELCHANGED);
SetFocus();
CWnd::OnLButtonDown(nFlags, point);
}
//-----------------------------------------------------------------------------
// Purpose: Notifies our parent window of a double-click event.
// Input : nFlags -
// point -
//-----------------------------------------------------------------------------
void CTextureWindow::OnLButtonDblClk(UINT nFlags, CPoint point)
{
GetParent()->PostMessage(TWN_LBUTTONDBLCLK);
CWnd::OnLButtonDblClk(nFlags, point);
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : nChar -
// nRepCnt -
// nFlags -
//-----------------------------------------------------------------------------
void CTextureWindow::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
CWnd::OnKeyDown(nChar, nRepCnt, nFlags);
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : nFlags -
// zDelta -
// point -
// Output : Returns TRUE on success, FALSE on failure.
//-----------------------------------------------------------------------------
BOOL CTextureWindow::OnMouseWheel(UINT nFlags, short zDelta, CPoint point)
{
int nScrollCode;
if (zDelta > 0)
{
nScrollCode = SB_LINEUP;
}
else
{
nScrollCode = SB_LINEDOWN;
}
SCROLLINFO si;
GetScrollInfo(SB_VERT, &si);
int iCurPos = GetScrollPos(SB_VERT);
int iLimit = GetScrollLimit(SB_VERT);
int iPos = int(si.nPos);
switch (nScrollCode)
{
case SB_LINEUP:
{
iPos = -(int)si.nPage / 4;
break;
}
case SB_LINEDOWN:
{
iPos = si.nPage / 4;
break;
}
}
if (iCurPos + iPos < 0)
{
iPos = -iCurPos;
}
if (iCurPos + iPos > iLimit)
{
iPos = iLimit - iCurPos;
}
if (iPos)
{
SetScrollPos(SB_VERT, iCurPos + iPos);
ScrollWindow(0, -iPos);
UpdateWindow();
}
return(TRUE);
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : nChar -
// nRepCnt -
// nFlags -
//-----------------------------------------------------------------------------
void CTextureWindow::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
{
CWnd::OnChar(nChar, nRepCnt, nFlags);
}
//-----------------------------------------------------------------------------
// Purpose: Sets the contents of the texture window to a specific list of textures.
// Input : pList - Textures with which to populate the texture window.
//-----------------------------------------------------------------------------
void CTextureWindow::SetSpecificList(TextureWindowTexList *pList)
{
m_pSpecificList = pList;
if (m_hWnd != NULL)
{
UpdateScrollSizes();
SelectTexture(szCurTexture, FALSE);
RedrawWindow();
}
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : eTextureFormat -
//-----------------------------------------------------------------------------
void CTextureWindow::SetTextureFormat(TEXTUREFORMAT eTextureFormat)
{
m_eTextureFormat = eTextureFormat;
}