//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ==== // // Purpose: // //============================================================================= #include "stdafx.h" #pragma warning(push, 1) #pragma warning(disable:4701 4702 4530) #include #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 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; }