/* Copyright (C) 1999-2007 id Software, Inc. and contributors. For a list of contributors, see the accompanying CONTRIBUTORS file. This file is part of GtkRadiant. GtkRadiant is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. GtkRadiant is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GtkRadiant; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ // // Texture Window // // Leonardo Zide (leo@lokigames.com) // /*!\todo Clean up texture menu. - Remove all global variables and use some objects instead. - Create an interface for a plugin to add texture menu items. - Make sure the interface is not dependent on gtk. */ #ifdef _WIN32 //#include #include #endif #if defined (__linux__) || defined (__APPLE__) #include #include #endif #include #include #include #include #include "stdafx.h" #include "texwindow.h" #include "str.h" #include "missing.h" #include "texmanip.h" #define TYP_MIPTEX 68 static unsigned tex_palette[256]; #define FONT_HEIGHT 10 //int texture_mode = GL_NEAREST; //int texture_mode = GL_NEAREST_MIPMAP_NEAREST; //int texture_mode = GL_NEAREST_MIPMAP_LINEAR; //int texture_mode = GL_LINEAR; //int texture_mode = GL_LINEAR_MIPMAP_NEAREST; int texture_mode = GL_LINEAR_MIPMAP_LINEAR; int g_nTextureOffset = 0; // current active texture directory //++timo FIXME: I'm not sure this is used anymore char texture_directory[128]; // if true, the texture window will only display in-use shaders // if false, all the shaders in memory are displayed qboolean g_bShowAllShaders; bool g_bFilterEnabled = false; CString g_strFilter; // texture layout functions // TTimo: now based on shaders int nActiveShadersCount; int nCurrentShader; IShader* pCurrentShader; qtexture_t *current_texture = NULL; int current_x, current_y, current_row; // globals for textures int texture_nummenus; char texture_menunames[MAX_TEXTUREDIRS][128]; // the list of scripts/*.shader files we need to work with // those are listed in shaderlist file // FIXME TTimo I get the feeling that those would need to move to the shaders module // for now it's still more simple to just keep it here GSList *l_shaderfiles = NULL; void SelectTexture (int mx, int my, bool bShift, bool bFitScale=false); void Texture_MouseDown (int x, int y, int buttons); void Texture_MouseMoved (int x, int y, int buttons); CPtrArray g_lstSkinCache; // TTimo: modifed to add a qtexture_t, Texture_LoadSkin loads using the shader API / QERApp_TryTexture_ForName // m_strName is a copy of qtex->name struct SkinInfo { CString m_strName; int m_nTextureBind; qtexture_t *m_qtex; SkinInfo(const char *pName, int n, qtexture_t *qtex) { m_strName = pName; m_nTextureBind = n; m_qtex = qtex; }; SkinInfo(){}; }; // ============================================================================= // global functions // gets active texture extension // // FIXME: fix this to be generic from project file // int GetTextureExtensionCount() { // hardcoded hack for png support if (g_pGameDescription->mGameFile == "sof2.game") return 3; else return 2; } const char* GetTextureExtension(int nIndex) { switch(nIndex) { case 0: return "tga"; break; case 1: return "jpg"; break; case 2: return "png"; break; default: return NULL; } } /* ============== Texture_InitPalette ============== */ void Texture_InitPalette (byte *pal) { int r,g,b; int i; int inf; byte gammatable[256]; float gamma; gamma = g_qeglobals.d_savedinfo.fGamma; if (gamma == 1.0) { for (i=0 ; i<256 ; i++) gammatable[i] = i; } else { for (i=0 ; i<256 ; i++) { inf = (int)( 255.0f * pow( ( i + 0.5f ) / 255.5f , gamma ) + 0.5f ); if (inf < 0) inf = 0; if (inf > 255) inf = 255; gammatable[i] = inf; } } for (i=0 ; i<256 ; i++) { r = gammatable[pal[0]]; g = gammatable[pal[1]]; b = gammatable[pal[2]]; pal += 3; //v = (r<<24) + (g<<16) + (b<<8) + 255; //v = BigLong (v); //tex_palette[i] = v; tex_palette[i*3+0] = r; tex_palette[i*3+1] = g; tex_palette[i*3+2] = b; } } void SetTexParameters (void) { qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, texture_mode ); switch ( texture_mode ) { case GL_NEAREST: case GL_NEAREST_MIPMAP_NEAREST: case GL_NEAREST_MIPMAP_LINEAR: qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); break; case GL_LINEAR: case GL_LINEAR_MIPMAP_NEAREST: case GL_LINEAR_MIPMAP_LINEAR: qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); break; } } /* ============ Texture_SetMode ============ */ void Texture_SetMode(int iMenu) { int iMode; qboolean texturing = true; gpointer item = NULL; switch (iMenu) { case ID_VIEW_NEAREST: item = g_object_get_data (G_OBJECT (g_pParentWnd->m_pWidget), "menu_view_nearest"); iMode = GL_NEAREST; break; case ID_VIEW_NEARESTMIPMAP: item = g_object_get_data (G_OBJECT (g_pParentWnd->m_pWidget), "menu_view_nearestmipmap"); iMode = GL_NEAREST_MIPMAP_NEAREST; break; case ID_VIEW_LINEAR: item = g_object_get_data (G_OBJECT (g_pParentWnd->m_pWidget), "menu_view_linear"); iMode = GL_LINEAR; break; case ID_VIEW_BILINEAR: item = g_object_get_data (G_OBJECT (g_pParentWnd->m_pWidget), "menu_view_bilinear"); iMode = GL_NEAREST_MIPMAP_LINEAR; break; case ID_VIEW_BILINEARMIPMAP: item = g_object_get_data (G_OBJECT (g_pParentWnd->m_pWidget), "menu_view_bilinearmipmap"); iMode = GL_LINEAR_MIPMAP_NEAREST; break; case ID_VIEW_TRILINEAR: item = g_object_get_data (G_OBJECT (g_pParentWnd->m_pWidget), "menu_view_trilinear"); iMode = GL_LINEAR_MIPMAP_LINEAR; break; case ID_TEXTURES_WIREFRAME: item = g_object_get_data (G_OBJECT (g_pParentWnd->m_pWidget), "menu_view_wireframe"); iMode = -1; texturing = false; break; case ID_TEXTURES_FLATSHADE: item = g_object_get_data (G_OBJECT (g_pParentWnd->m_pWidget), "menu_view_flatshade"); iMode = -1; texturing = false; break; default: return; } g_qeglobals.d_savedinfo.iTexMenu = iMenu; // NOTE: texture_mode is a GLenum used directly in glTexParameter if(iMode!=-1) texture_mode = iMode; g_bIgnoreCommands++; if (item != NULL) gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), TRUE); g_bIgnoreCommands--; if (texturing) SetTexParameters (); if ( !texturing && iMenu == ID_TEXTURES_WIREFRAME) { g_pParentWnd->GetCamWnd()->Camera()->draw_mode = cd_wire; Map_BuildBrushData(); Sys_UpdateWindows (W_ALL); return; } else if ( !texturing && iMenu == ID_TEXTURES_FLATSHADE) { g_pParentWnd->GetCamWnd()->Camera()->draw_mode = cd_solid; Map_BuildBrushData(); Sys_UpdateWindows (W_ALL); return; } for (qtexture_t *q = g_qeglobals.d_qtextures; q; q = q->next) { qglBindTexture (GL_TEXTURE_2D, q->texture_number); SetTexParameters (); } // select the default texture qglBindTexture( GL_TEXTURE_2D, 0 ); qglFinish(); if (g_pParentWnd->GetCamWnd()->Camera()->draw_mode != cd_texture) { g_pParentWnd->GetCamWnd()->Camera()->draw_mode = cd_texture; Map_BuildBrushData(); } Sys_UpdateWindows (W_ALL); } /*! gamma correction stuff took out of QERApp_LoadTextureRGBA for clarity */ byte g_gammatable[256]; void ResampleGamma(float fGamma) { int i,inf; if (fGamma == 1.0) { for (i = 0; i < 256; i++) g_gammatable[i] = i; } else { for (i = 0; i < 256; i++) { inf = (int)( 255.0f * pow( (i + 0.5f) / 255.5f , fGamma ) + 0.5f ); if (inf < 0) inf = 0; if (inf > 255) inf = 255; g_gammatable[i] = inf; } } } /*! this function does the actual processing of raw RGBA data into a GL texture it will also generate the mipmaps it looks like pPixels nWidth nHeight are the only relevant parameters */ qtexture_t *QERApp_LoadTextureRGBA(unsigned char* pPixels, int nWidth, int nHeight) { static float fGamma = -1; float total[3]; byte *outpixels = NULL; int i, j, resampled, width2, height2, width3, height3; int max_tex_size = 0, mip = 0; int nCount = nWidth * nHeight; if (fGamma != g_qeglobals.d_savedinfo.fGamma) { fGamma = g_qeglobals.d_savedinfo.fGamma; ResampleGamma(fGamma); } qglGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_tex_size); if (!max_tex_size) max_tex_size = 1024; qtexture_t *q = (qtexture_t*)g_malloc(sizeof(*q)); q->width = nWidth; q->height = nHeight; total[0] = total[1] = total[2] = 0.0f; // resample texture gamma according to user settings for (i = 0; i < (nCount * 4); i += 4) { for (j = 0; j < 3; j++) { total[j] += (pPixels + i)[j]; byte b = (pPixels + i)[j]; (pPixels + i)[j] = g_gammatable[b]; } } q->color[0] = total[0] / (nCount * 255); q->color[1] = total[1] / (nCount * 255); q->color[2] = total[2] / (nCount * 255); qglGenTextures (1, &q->texture_number); qglBindTexture( GL_TEXTURE_2D, q->texture_number ); SetTexParameters(); width2 = 1; while (width2 < nWidth) width2 <<= 1; height2 = 1; while (height2 < nHeight) height2 <<= 1; width3 = width2; height3 = height2; while (width3 > max_tex_size) width3 >>= 1; while (height3 > max_tex_size) height3 >>= 1; if (width3 < 1) width3 = 1; if (height3 < 1) height3 = 1; if (!(width2 == nWidth && height2 == nHeight)) { resampled = 1; outpixels = (byte *)malloc(width2 * height2 * 4); R_ResampleTexture(pPixels, nWidth, nHeight, outpixels, width2, height2, 4); } else { resampled = 0; outpixels = pPixels; } while (width2 > width3 || height2 > height3) { GL_MipReduce(outpixels, outpixels, width2, height2, width3, height3); if (width2 > width3) width2 >>= 1; if (height2 > height3) height2 >>= 1; } qglTexImage2D(GL_TEXTURE_2D, mip++, g_qeglobals.texture_components, width2, height2, 0, GL_RGBA, GL_UNSIGNED_BYTE, outpixels); while (width2 > 1 || height2 > 1) { GL_MipReduce(outpixels, outpixels, width2, height2, 1, 1); if (width2 > 1) width2 >>= 1; if (height2 > 1) height2 >>= 1; qglTexImage2D(GL_TEXTURE_2D, mip++, g_qeglobals.texture_components, width2, height2, 0, GL_RGBA, GL_UNSIGNED_BYTE, outpixels); } qglBindTexture(GL_TEXTURE_2D, 0); if (resampled) free(outpixels); return q; } /* ================== DumpUnreferencedShaders usefull function: dumps the list of .shader files that are not referenced to the console ================== */ void DumpUnreferencedShaders() { GSList *lst, *sh, *files; bool bFound = false; files = vfsGetFileList ("scripts", "shader"); for (lst = files; lst; lst = lst->next) { bool listed = false; for (sh = l_shaderfiles; sh != NULL; sh = g_slist_next (sh)) if (!strcmp ((char*)sh->data, (char*)lst->data)) { listed = true; break; } if (!listed) { if (!bFound) { bFound = true; Sys_FPrintf (SYS_WRN, "Following shader files are not referenced in shaderlist.txt:\n"); } Sys_FPrintf (SYS_WRN, "%s\n", (char*)lst->data); } } vfsClearFileDirList (&files); } /* ================== BuildShaderList build a CStringList of shader names ================== */ void BuildShaderList() { int count; char filename[1024]; char *pBuff; char dirstring[NAME_MAX]; int nLen; if (l_shaderfiles!=NULL) { g_slist_free(l_shaderfiles); l_shaderfiles = NULL; } if (g_pGameDescription->mGameFile != "hl.game") { strcpy(filename, g_pGameDescription->mShaderlist.GetBuffer()); count = vfsGetFileCount(filename, 0 ); if (count==0) { Sys_FPrintf(SYS_ERR, "Couldn't find '%s'\n", g_pGameDescription->mShaderlist.GetBuffer()); return; } // NOTE TTimo we use vfsGetFullPath solely to get the full path of the shader list we are gonna load // but we actually send the relative path to vfsLoadFile // so let's hope there is no disparity between the two functions if (!vfsGetFullPath(filename, 0, 0)) { Sys_FPrintf(SYS_ERR, "Couldn't find full path for '%s'\n", g_pGameDescription->mShaderlist.GetBuffer()); Sys_FPrintf(SYS_ERR, "did you hit bug http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=130 ?\n"); return; } Sys_Printf("Parsing shader files from %s\n", vfsGetFullPath(filename, 0, 0)); nLen = vfsLoadFile (filename, reinterpret_cast(&pBuff), 0); if (nLen > 0) { StartTokenParsing(pBuff); nLen = 0; while (GetToken(true)) { GSList *tmp; bool found = false; // each token should be a shader filename sprintf(dirstring, "%s.shader", token); for (tmp = l_shaderfiles; tmp != NULL; tmp = tmp->next) { if (!strcmp (dirstring, (char*)tmp->data)) { found = true; Sys_FPrintf(SYS_WRN, "duplicate entry \"%s\" in shaderlist.txt\n", (char*)tmp->data); break; } } if (!found) { l_shaderfiles = g_slist_append (l_shaderfiles, strdup (dirstring)); nLen++; } } g_free(pBuff); } } } /* ================== FillTextureMenu ================== */ void ClearGSList (GSList* lst) { GSList *p = lst; while (p) { free (p->data); p = g_slist_remove (p, p->data); } } void FillTextureMenu (GSList** pArray) { GtkWidget *menu, *sep, *item; // point to the Textures GtkMenu and to the last separator GList *lst; GSList *texdirs = NULL; GSList *texdirs_tmp = NULL; GSList *p; char dirRoot[NAME_MAX]; // delete everything menu = GTK_WIDGET (g_object_get_data (G_OBJECT (g_qeglobals_gui.d_main_window), "menu_textures")); sep = GTK_WIDGET (g_object_get_data (G_OBJECT (g_qeglobals_gui.d_main_window), "menu_textures_separator")); lst = g_list_find (gtk_container_children (GTK_CONTAINER (menu)), sep); while (lst->next) { // these delete functions are recursive, it's gonna free all submenus gtk_widget_destroy (GTK_WIDGET (lst->next->data)); // lst is no longer relevant, need to get it again lst = g_list_find (gtk_container_children (GTK_CONTAINER (menu)), sep); } texture_nummenus = 0; // add everything if (!g_qeglobals.d_project_entity) return; // scan texture dirs and pak files only if not restricting to shaderlist if (!g_PrefsDlg.m_bTexturesShaderlistOnly) { texdirs_tmp = vfsGetDirList ("textures/"); for (p=texdirs_tmp; p; p=g_slist_next(p)) { // Hydra: erm, this didn't used to do anything except leak memory... // For Halflife support this is required to work however. // g_slist_append(texdirs, p->data); texdirs = g_slist_append(texdirs, strdup((char *)p->data)); } vfsClearFileDirList (&texdirs_tmp); } // scan the shaders in shaderlist.txt BuildShaderList (); PreloadShaders (); DumpUnreferencedShaders (); while (l_shaderfiles != NULL) { char shaderfile[PATH_MAX]; gboolean found = FALSE; ExtractFileName ((char*)l_shaderfiles->data, shaderfile); StripExtension (shaderfile); g_strdown (shaderfile); for (GSList *tmp = texdirs; tmp; tmp = g_slist_next (tmp)) if (!strcasecmp ((char*)tmp->data, shaderfile)) { found = TRUE; break; } if (!found) texdirs = g_slist_prepend (texdirs, strdup (shaderfile)); free (l_shaderfiles->data); l_shaderfiles = g_slist_remove (l_shaderfiles, l_shaderfiles->data); } // sort the list texdirs = g_slist_sort (texdirs, (GCompareFunc)strcmp); GSList *temp = texdirs; while (temp) { char* ptr = strchr ((char*)temp->data, '_'); // do we shrink the menus? if (ptr != NULL) { // extract the root strcpy (dirRoot, (char*)temp->data); dirRoot[ptr - (char*)temp->data + 1] = 0; // we shrink only if we have at least two things to shrink :-) if (temp->next && (strstr ((char*)temp->next->data, dirRoot) == (char*)temp->next->data)) { GtkWidget *pSubMenu = gtk_menu_new (); GtkWidget *pSubMenuRef = pSubMenu; // keep going... do { item = gtk_menu_item_new_with_label ((char*)temp->data); gtk_widget_show (item); CheckMenuSplitting (pSubMenu); gtk_container_add (GTK_CONTAINER (pSubMenu), item); gtk_signal_connect (GTK_OBJECT (item), "activate", GTK_SIGNAL_FUNC (HandleCommand), GINT_TO_POINTER (CMD_TEXTUREWAD+texture_nummenus)); strcpy (texture_menunames[texture_nummenus], (char*)temp->data); strcat (texture_menunames[texture_nummenus], "/"); if (pArray) *pArray = g_slist_append (*pArray, strdup ((char*)temp->data)); if (++texture_nummenus == MAX_TEXTUREDIRS) { Sys_Printf("WARNING: max texture directories count has been reached!\n"); // push submenu and get out item = gtk_menu_item_new_with_label (dirRoot); gtk_widget_show (item); gtk_container_add (GTK_CONTAINER (menu), item); gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), pSubMenu); ClearGSList (texdirs); return; } temp = temp->next; } while (temp && (strstr((char*)temp->data, dirRoot)==temp->data)); ptr = strchr (dirRoot, '_'); *ptr = 0; item = gtk_menu_item_new_with_label (dirRoot); gtk_widget_show (item); CheckMenuSplitting (menu); gtk_container_add (GTK_CONTAINER (menu), item); gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), pSubMenuRef); continue; } } item = gtk_menu_item_new_with_label ((char*)temp->data); gtk_widget_show (item); CheckMenuSplitting (menu); gtk_container_add (GTK_CONTAINER (menu), item); gtk_signal_connect (GTK_OBJECT (item), "activate", GTK_SIGNAL_FUNC (HandleCommand), GINT_TO_POINTER (CMD_TEXTUREWAD+texture_nummenus)); strcpy (texture_menunames[texture_nummenus], (char*)temp->data); strcat (texture_menunames[texture_nummenus], "/"); if (pArray) *pArray = g_slist_append (*pArray, strdup ((char*)temp->data)); if (++texture_nummenus == MAX_TEXTUREDIRS) { Sys_Printf("WARNING: max texture directories count has been reached!\n"); ClearGSList (texdirs); return; } temp = temp->next; } ClearGSList (texdirs); } /* ============== Texture_ShowDirectory relies on texture_directory global for the directory to use called by void Texture_ShowDirectory (int menunum, bool bLinked) void Texture_ShowDirectory (char* pPath, bool bLinked) 1) Load the shaders for the given directory 2) Scan the remaining texture, load them and assign them a default shader (the "noshader" shader) NOTE: when writing a texture plugin, or some texture extensions, this function may need to be overriden, and made available through the IShaders interface NOTE: for texture window layout: all shaders are stored with alphabetical order after load previously loaded and displayed stuff is hidden, only in-use and newly loaded is shown ( the GL textures are not flushed though) ============== */ void Texture_ShowDirectory () { char name[1024]; char dirstring[1024]; CString strTemp; int shaders_count = 0; int textures_count = 0; GSList *files = NULL, *temp; g_bScreenUpdates = false; // refresh the in-use textures: that will clear the IsDisplayed flag on unused stuff // and leave it on in-use so they'll still be displayed Texture_ShowInuse(); // and textures loaded in the following lines will be displayed as well... // NOTE: shaders that are not in use but have been loaded previously are still in memory. But they don't get displayed. g_qeglobals.d_texturewin.originy = 0; // load texture_directory.shader // NOTE: because of above call to Texture_ClearInuse, g_ActiveShaders will have the newly loaded shaders only // we'll use that later to check if textures have a shader associated or not // NOTE: all shaders loaded through QERApp_LoadShadersFromDir will get their InUse flag to True, we'll need a call to Texture_ShowInUse for later cleanup/adjustment // NOTE: QERApp_LoadShadersFromDir has two criterions for loading a shader: // the shaderfile is texture_directory (like "museum" will load everything in museum.shader) // the shader name contains texture_directory (like "base_floor" will load museum.shader::base_floor/concfloor_rain) shaders_count = QERApp_LoadShadersFromDir(texture_directory); // load remaining texture files // if a texture is already in use to represent a shader, ignore it // need this function "GSList *lst SynapseServer::GetMinorList(char *major_name);" sprintf (dirstring, "textures/%s", texture_directory); g_ImageManager.BeginExtensionsScan(); const char* ext; while((ext=g_ImageManager.GetNextExtension()) != NULL) { files = g_slist_concat(files, vfsGetFileList (dirstring, ext)); } for (temp = files; temp; temp = temp->next) { sprintf(name, "%s%s", texture_directory, (char*)temp->data); StripExtension (name); strTemp = name; strTemp.MakeLower(); if (strTemp.Find(".specular") >= 0 || strTemp.Find(".glow") >= 0 || strTemp.Find(".bump") >= 0 || strTemp.Find(".diffuse") >= 0 || strTemp.Find(".blend") >= 0 || strTemp.Find(".alpha") >= 0) continue; // avoid ever loading a texture name with spaces if (strTemp.Find(" ") >= 0) { Sys_FPrintf(SYS_WRN, "WARNING: Skipping texture name with spaces [%s]\n", strTemp.GetBuffer()); continue; } // build a texture name that fits the conventions for qtexture_t::name char stdName[1024]; sprintf( stdName, "textures/%s", name ); // check if this texture doesn't have a shader if (!QERApp_ActiveShader_ForTextureName( stdName )) { QERApp_CreateShader_ForTextureName (stdName); textures_count++; } } Sys_Printf("Loaded %d shaders and created default shader for %d orphan textures.\n", shaders_count, textures_count ); vfsClearFileDirList (&files); // sort for displaying QERApp_SortActiveShaders(); sprintf (name, "Textures: %s", texture_directory); gtk_window_set_title (GTK_WINDOW (g_qeglobals_gui.d_entity), name); // select the first texture in the list if (!g_qeglobals.d_texturewin.texdef.GetName()[0]) SelectTexture (16, g_qeglobals.d_texturewin.height -16, false); g_bScreenUpdates = true; Sys_UpdateWindows (W_TEXTURE); } /* ============== Texture_ShowDirectory 1) Load the shaders for the given directory 2) Scan the remaining texture, load them and assign them a default shader (the "noshader" shader) NOTE: when writing a texture plugin, or some texture extensions, this function may need to be overriden, and made available through the IShaders interface ============== */ void Texture_ShowDirectory (int menunum) { strcpy (texture_directory, texture_menunames[menunum-CMD_TEXTUREWAD]); Texture_ShowDirectory(); } // scroll origin so the current texture is completely on screen // if current texture is not displayed, nothing is changed void Texture_ResetPosition() { qtexture_t *q; int x,y; //this shouldn't ever happen, we startup with notex if (!g_qeglobals.d_texturewin.texdef.GetName()[0]) { return; } // otherwise position with current texture shown // this used to be in Texture_SetTexture Texture_StartPos (); while (1) { // NOTE: return value is == pCurrentShader and pCurrentShader->getTexture == current_texture Texture_NextPos (&x, &y); q = current_texture; // if the current texture never found (because // 'show shaders' is off, // for example), do nothing if (!q) break; int nHeight = (int)(q->height * ((float)g_PrefsDlg.m_nTextureScale / 100)); // we have found when texdef->name and the shader name match // NOTE: as everywhere else for our comparisons, we are not case sensitive if (!strcmpi( g_qeglobals.d_texturewin.texdef.GetName(), pCurrentShader->getName() )) { // take care of calls before initialized if ( !g_qeglobals.d_texturewin.height) { g_qeglobals.d_texturewin.originy = 0; break; } // if the bottom of our selected texture will fit with origin 0, use that // to prevent scrolling uglyness (stuff scrolled off screen when // everything would fit) if ( -(y -nHeight-2*FONT_HEIGHT) < g_qeglobals.d_texturewin.height) { g_qeglobals.d_texturewin.originy = 0; break; } // if current is off the top of the window, move it to the top if (y > g_qeglobals.d_texturewin.originy) { g_qeglobals.d_texturewin.originy = y; break; } // if current is off the bottom, put it on the bottom if (y-nHeight-2*FONT_HEIGHT < g_qeglobals.d_texturewin.originy-g_qeglobals.d_texturewin.height) { g_qeglobals.d_texturewin.originy = y-nHeight-2*FONT_HEIGHT+g_qeglobals.d_texturewin.height; break; } // if we made it here, it should already be in view break; } } Sys_UpdateWindows (W_TEXTURE); } /* ============== Texture_ShowAll will set the IsDisplayed flag on all the active shaders, so we see everything that's currently in memory ============== */ void Texture_ShowAll() { char name[1024]; #ifdef _DEBUG if (g_bShowAllShaders) Sys_Printf("WARNING: already showing all shaders\n"); #endif QERApp_ActiveShaders_SetDisplayed(true); g_bShowAllShaders = true; // put some information in the texture window title? sprintf (name, "Textures: in use"); gtk_window_set_title (GTK_WINDOW (g_qeglobals_gui.d_entity), name); Sys_UpdateWindows (W_TEXTURE); } /* ============== Texture_ShowInuse clear all IsDisplayed flags scan the map, set IsInUse (will set IsDisplayed on the way) NOTE: don't sort the textures, don't update the windows (it's used in several contexts, not always necessary to do either) ============== */ void WINAPI Texture_ShowInuse (void) { face_t *f; brush_t *b; char name[1024]; g_qeglobals.d_texturewin.originy = 0; // purge QERApp_ActiveShaders_SetDisplayed(false); // scan and only display in-use stuff Sys_Status("Selecting active textures", 0); for (b=active_brushes.next ; b != NULL && b != &active_brushes ; b=b->next) { if (b->patchBrush) { b->pPatch->pShader->SetInUse(true); } else { for (f=b->brush_faces ; f ; f=f->next) { f->pShader->SetInUse(true); } } } for (b=selected_brushes.next ; b != NULL && b != &selected_brushes ; b=b->next) { if (b->patchBrush) { b->pPatch->pShader->SetInUse(true); } else { for (f=b->brush_faces ; f ; f=f->next) { f->pShader->SetInUse(true); } } } // we are no longer showing everything g_bShowAllShaders = false; // put some information in the texture window title? sprintf (name, "Textures: in use"); gtk_window_set_title (GTK_WINDOW (g_qeglobals_gui.d_entity), name); // select the first texture in the list if (!g_qeglobals.d_texturewin.texdef.GetName()[0]) { SelectTexture (16, g_qeglobals.d_texturewin.height -16, false); } } void Texture_ShowStartupShaders() { if (g_PrefsDlg.m_nShader == PrefsDlg::SHADER_COMMON) { // RIANT // HACK FOR JK2 SUPPORT if (g_pGameDescription->mGameFile == "jk2.game" || g_pGameDescription->mGameFile == "ja.game") { strcpy (texture_directory, "system/"); } // RIANT // HACK FOR SOF2 SUPPORT else if (g_pGameDescription->mGameFile == "sof2.game") { strcpy (texture_directory, "tools/"); } else strcpy (texture_directory, "common/"); Texture_ShowDirectory (); } if (g_PrefsDlg.m_nShader == PrefsDlg::SHADER_ALL) { int count; char filename[1024]; char *pBuff; char dirstring[NAME_MAX]; int nLen; GSList *shaderfiles = NULL; strcpy(filename, g_pGameDescription->mShaderlist.GetBuffer()); count = vfsGetFileCount(filename, 0); if (count == 0) { Sys_FPrintf(SYS_ERR, "Couldn't find '%s'\n", g_pGameDescription->mShaderlist.GetBuffer()); return; } if (!vfsGetFullPath(filename, 0, 0)) { Sys_FPrintf(SYS_ERR, "Couldn't find full path for '%s'\n", g_pGameDescription->mShaderlist.GetBuffer()); Sys_FPrintf(SYS_ERR, "did you hit bug http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=130 ?\n"); return; } Sys_Printf("Parsing shader files from %s\n", vfsGetFullPath(filename, 0, 0)); nLen = vfsLoadFile (filename, reinterpret_cast(&pBuff), 0); if (nLen > 0) { StartTokenParsing(pBuff); nLen = 0; while (GetToken(true)) { GSList *tmp; bool found = false; // each token should be a shader filename sprintf(dirstring, "%s.shader", token); for (tmp = shaderfiles; tmp != NULL; tmp = tmp->next) { if (!strcmp (dirstring, (char*)tmp->data)) { found = true; Sys_FPrintf(SYS_WRN, "duplicate entry \"%s\" in shaderlist.txt\n", (char*)tmp->data); break; } } if (!found) { shaderfiles = g_slist_append (l_shaderfiles, strdup (dirstring)); strcpy (texture_directory, dirstring); Texture_ShowDirectory (); nLen++; } } g_free(pBuff); } } } /* ============================================================================ TEXTURE LAYOUT TTimo: now based on a rundown through all the shaders nActiveShadersCount: number of shader that have a qtexture_t and may be displayed in the tex window nCurrentShader: index of active shader that has the current_texture pCurrentShader: IShader* for current shader NOTE: we expect the Active shaders count doesn't change during a Texture_StartPos .. Texture_NextPos cycle otherwise we may need to rely on a list instead of an array storage ============================================================================ */ void Texture_StartPos (void) { //++timo TODO: check use of current_texture and current_row? current_x = 8; current_y = -8; current_row = 0; nActiveShadersCount = QERApp_GetActiveShaderCount(); nCurrentShader = -1; current_texture = NULL; pCurrentShader = NULL; } // if texture_showinuse jump over non in-use textures // it's not very clear what should be done here and what in Texture_Draw .. maybe merging the two would do good IShader* Texture_NextPos (int *x, int *y) { qtexture_t* q; while (1) { if (nCurrentShader >= nActiveShadersCount - 1) { // no more shaders current_texture = NULL; pCurrentShader = NULL; return NULL; } nCurrentShader++; pCurrentShader = QERApp_ActiveShader_ForIndex(nCurrentShader); if (pCurrentShader == NULL) { Sys_Printf("ERROR: unexpected pCurrentShader == NULL in Texture_NextPos\n"); return NULL; } current_texture = pCurrentShader->getTexture(); q = current_texture; if (!q) { Sys_Printf("WARNING: found an IShader without qtexture_t in Texture_NextPos\n"); return NULL; } /* Never show anything other than "textures/" path, This is for q1/q2/q3 .map format, which expects "textures/" path on everything we apply */ if(strncmp(pCurrentShader->getName(), "textures/", 9) != 0) continue; // don't show shaders? if (!(g_PrefsDlg.m_bShowShaders || pCurrentShader->IsDefault())) continue; if (g_PrefsDlg.m_bTextureWindow) { // some basic filtering if (!g_pParentWnd->GetTexWnd()->CheckFilter( pCurrentShader->getName() )) continue; } //++timo FIXME: texture_showinuse is useless? with the menu and reload we just refresh the IsDisplayed flag // but the IsInUse is only relevant to draw the green outline if (pCurrentShader->IsDisplayed()) break; continue; } int nWidth = (int)(q->width * ((float)g_PrefsDlg.m_nTextureScale / 100)); int nHeight = (int)(q->height * ((float)g_PrefsDlg.m_nTextureScale / 100)); if (current_x + nWidth > g_qeglobals.d_texturewin.width-8 && current_row) { // go to the next row unless the texture is the first on the row current_x = 8; current_y -= current_row + FONT_HEIGHT + 4; current_row = 0; } *x = current_x; *y = current_y; // Is our texture larger than the row? If so, grow the // row height to match it if (current_row < nHeight) current_row = nHeight; // never go less than 64, or the names get all crunched up current_x += nWidth < 64 ? 64 : nWidth; current_x += 8; return pCurrentShader; } /* ============================================================================ MOUSE ACTIONS ============================================================================ */ static int textures_cursorx, textures_cursory; /* ============ Texture_SetTexture brushprimit_texdef must be understood as a qtexture_t with width=2 height=2 ( the default one ) ============ */ //++timo NOTE: this is a mix of Shader module stuff and texture explorer // it might need to be split in parts or moved out .. dunno void WINAPI Texture_SetTexture (texdef_t *texdef, brushprimit_texdef_t *brushprimit_texdef, bool bFitScale, IPluginTexdef *pTexdef, bool bSetSelection ) { if (texdef->GetName()[0] == '(') { Sys_Status("Can't select an entity texture", 0); return; } g_qeglobals.d_texturewin.texdef = *texdef; g_qeglobals.d_texturewin.texdef.flags &= ~SURF_KEEP; g_qeglobals.d_texturewin.texdef.contents &= ~CONTENTS_KEEP; // store the shader pointer // NOTE: maybe passing the shader pointer would help? g_qeglobals.d_texturewin.pShader->DecRef(); g_qeglobals.d_texturewin.pShader = QERApp_Shader_ForName(texdef->GetName()); g_qeglobals.d_texturewin.pShader->IncRef(); // set this shader as in use g_qeglobals.d_texturewin.pShader->SetInUse( true ); // store the texture coordinates for new brush primitive mode // be sure that all the callers are using the default 2x2 texture if (g_qeglobals.m_bBrushPrimitMode) { g_qeglobals.d_texturewin.brushprimit_texdef = *brushprimit_texdef; } g_dlgFind.updateTextures(texdef->GetName()); if (!g_dlgFind.isOpen() && bSetSelection) { Select_SetTexture(texdef,brushprimit_texdef,bFitScale); } //plugins: send a message telling that the selected texture may have changed DispatchRadiantMsg( RADIANT_TEXTURE ); // scroll origin so the texture is completely on screen // takes texdef from g_qeglobals.d_texturewin.texdef, set above Texture_ResetPosition(); } void ViewShader(const char *pFile, const char *pName) { // ask the vfs to build the full path to the file // (i.e. the first one found) char *fullName = vfsGetFullPath(pFile,0,0); if (fullName == NULL) { Sys_FPrintf (SYS_ERR, "Couldn't get a full path to the shader file: %s\n", pFile); return; } char* pBuff = NULL; int nSize = vfsLoadFullPathFile(fullName, reinterpret_cast(&pBuff)); if (nSize <= 0) { Sys_FPrintf (SYS_ERR, "Failed to load shader file %s\n", fullName); return; } // look for the shader declaration int nStart; CString strFind = pName; CString strLook = pBuff; strLook.MakeLower(); strFind.MakeLower(); // offset used when jumping over commented out definitions int nOffset = 0; while (true) { nStart = strLook.Find(strFind, nOffset); if (nStart == -1) break; // we have found something, maybe it's a commented out shader name? char *strCheck = new char[strLook.GetLength()+1]; strcpy( strCheck, strLook.GetBuffer() ); strCheck[nStart] = 0; char *pCheck = strrchr( strCheck, '\n' ); // if there's a commentary sign in-between we'll continue if (pCheck && strstr( pCheck, "//" )) { delete[] strCheck; nOffset = nStart + 1; continue; } delete[] strCheck; nOffset = nStart; break; } // now close the file g_free(pBuff); DoTextEditor (fullName, nOffset); } /* ============== SelectTexture By mouse click ============== */ void SelectTexture (int mx, int my, bool bShift, bool bFitScale) { int x, y; qtexture_t *q; texdef_t tex; brushprimit_texdef_t brushprimit_tex; my += g_qeglobals.d_texturewin.originy-g_qeglobals.d_texturewin.height; Texture_StartPos (); while (1) { // NOTE: return value is == pCurrentShader and pCurrentShader->getTexture == current_texture Texture_NextPos (&x, &y); q = current_texture; if (!q) break; int nWidth = (int)(q->width * ((float)g_PrefsDlg.m_nTextureScale / 100)); int nHeight = (int)(q->height * ((float)g_PrefsDlg.m_nTextureScale / 100)); if (mx > x && mx - x < nWidth && my < y && y - my < nHeight + FONT_HEIGHT) { if (bShift) { if (pCurrentShader->IsDefault()) Sys_Printf("ERROR: %s is not a shader, it's a texture.\n", pCurrentShader->getName() ); else ViewShader( pCurrentShader->getShaderFileName(), pCurrentShader->getName() ); } else { memset (&tex, 0, sizeof(tex)); memset (&brushprimit_tex, 0, sizeof(brushprimit_tex)); if (g_qeglobals.m_bBrushPrimitMode) { // brushprimit fitted to a 2x2 texture brushprimit_tex.coords[0][0] = 1.0f; brushprimit_tex.coords[1][1] = 1.0f; } else { tex.scale[0] = g_pGameDescription->mTextureDefaultScale; tex.scale[1] = g_pGameDescription->mTextureDefaultScale; } tex.flags = pCurrentShader->getFlags(); // TTimo - shader code cleanup // texdef.name is the name of the shader, not the name of the actual texture file tex.SetName(pCurrentShader->getName()); // NOTE WARNING: Texture_SetTexture uses Texture_NextPos stuff to move the window position on to the texture // if there's some kind of fuckup in Texture_SetTexture you may end up with different pCurrentShader or even pCurrentShader == NULL // so we just consider pCurrentShader and current_texture are not valid after this point IShader *pAuxShader = pCurrentShader; Texture_SetTexture ( &tex, &brushprimit_tex, bFitScale, NULL); // Nurail CString strTex; CString strName; // if shader, print shader name, otherwise texture name //++timo FIXME: maybe CShader needs some properties between color / default / actual shader #ifdef _DEBUG // this one is never supposed to be set as current one if (pAuxShader->IsColor()) Sys_Printf("ERROR: unexpected pCurrentShader->IsColor() in SelectTexture\n"); #endif // NOTE: IsColor is false, IsDefault the only remaining property if (pAuxShader->IsDefault()) { strName = q->name; // remove the "textures/" if needed if (strName.Find("textures/")!=-1) strName = strName.Mid(9); } else { strName = pAuxShader->getName(); } strTex.Format("%s W: %i H: %i", strName.GetBuffer(), q->width, q->height); g_pParentWnd->SetStatusText(3, strTex); } return; } } Sys_Status("Did not select a texture", 0); } /* ============== Texture_MouseDown ============== */ void Texture_MouseDown (int x, int y, int buttons) { Sys_GetCursorPos (&textures_cursorx, &textures_cursory); // lbutton = select texture if (buttons == MK_LBUTTON || buttons == (MK_LBUTTON | MK_SHIFT) || buttons == (MK_LBUTTON | MK_CONTROL)) { SelectTexture (x, g_qeglobals.d_texturewin.height - 1 - y, buttons & MK_SHIFT, buttons & MK_CONTROL); UpdateSurfaceDialog(); UpdatePatchInspector(); } } /* ============== Texture_MouseMoved ============== */ void Texture_MouseMoved (int x, int y, int buttons) { int scale = 1; if ( buttons & MK_SHIFT ) scale = 4; // rbutton = drag texture origin if (buttons & MK_RBUTTON) { Sys_GetCursorPos (&x, &y); if ( y != textures_cursory) { g_qeglobals.d_texturewin.originy += ( y-textures_cursory) * scale; if (g_qeglobals.d_texturewin.originy > 0) g_qeglobals.d_texturewin.originy = 0; Sys_SetCursorPos (textures_cursorx, textures_cursory); // (g_PrefsDlg.m_bTextureScrollbar && g_qeglobals_gui.d_texture_scroll != NULL) // fixes broken texture scrolling when scrollbar is disabled GtkAdjustment *vadjustment = gtk_range_get_adjustment (GTK_RANGE (g_qeglobals_gui.d_texture_scroll)); gtk_adjustment_set_value (vadjustment, abs(g_qeglobals.d_texturewin.originy)); // } return; } } /* ============================================================================ DRAWING ============================================================================ */ int imax(int iFloor, int i) { if (i>iFloor) return iFloor;return i;} /* ============ Texture_Draw TTimo: relying on the shaders list to display the textures we must query all qtexture_t* to manage and display through the IShaders interface this allows a plugin to completely override the texture system ============ */ void Texture_Draw (int width, int height) { int x, y, last_y = 0, last_height = 0, nWidth, nHeight; qtexture_t *q; char *name; qglClearColor (g_qeglobals.d_savedinfo.colors[COLOR_TEXTUREBACK][0], g_qeglobals.d_savedinfo.colors[COLOR_TEXTUREBACK][1], g_qeglobals.d_savedinfo.colors[COLOR_TEXTUREBACK][2], 0); qglViewport (0,0,width,height); qglMatrixMode(GL_PROJECTION); qglLoadIdentity (); qglClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); qglDisable (GL_DEPTH_TEST); qglDisable(GL_BLEND); qglOrtho (0, width, g_qeglobals.d_texturewin.originy-height, g_qeglobals.d_texturewin.originy, -100, 100); qglEnable (GL_TEXTURE_2D); qglPolygonMode (GL_FRONT_AND_BACK, GL_FILL); g_qeglobals.d_texturewin.width = width; g_qeglobals.d_texturewin.height = height; Texture_StartPos(); for (;;) { // NOTE: return value is == pCurrentShader and pCurrentShader->getTexture == current_texture Texture_NextPos (&x, &y); q = current_texture; if (!q) break; nWidth = (int)(q->width * ((float)g_PrefsDlg.m_nTextureScale / 100)); nHeight = (int)(q->height * ((float)g_PrefsDlg.m_nTextureScale / 100)); if (y != last_y) { last_y = y; last_height = 0; } last_height = MAX (nHeight, last_height); // Is this texture visible? if ((y-nHeight-FONT_HEIGHT < g_qeglobals.d_texturewin.originy) && (y > g_qeglobals.d_texturewin.originy - height)) { // borders rules: // if it's the current texture, draw a thick red line, else: // shaders have a white border, simple textures don't // if !texture_showinuse: (some textures displayed may not be in use) // draw an additional square around with 0.5 1 0.5 color if (!strcmpi(g_qeglobals.d_texturewin.texdef.GetName(), pCurrentShader->getName())) { qglLineWidth (3); qglColor3f (1,0,0); qglDisable (GL_TEXTURE_2D); qglBegin (GL_LINE_LOOP); qglVertex2f (x-4,y-FONT_HEIGHT+4); qglVertex2f (x-4,y-FONT_HEIGHT-nHeight-4); qglVertex2f (x+4+nWidth,y-FONT_HEIGHT-nHeight-4); qglVertex2f (x+4+nWidth,y-FONT_HEIGHT+4); qglEnd (); qglEnable (GL_TEXTURE_2D); qglLineWidth (1); } else { qglLineWidth (1); // shader border: if (!pCurrentShader->IsDefault()) { qglColor3f (1,1,1); qglDisable (GL_TEXTURE_2D); qglBegin (GL_LINE_LOOP); qglVertex2f (x-1,y+1-FONT_HEIGHT); qglVertex2f (x-1,y-nHeight-1-FONT_HEIGHT); qglVertex2f (x+1+nWidth,y-nHeight-1-FONT_HEIGHT); qglVertex2f (x+1+nWidth,y+1-FONT_HEIGHT); qglEnd (); qglEnable (GL_TEXTURE_2D); } // highlight in-use textures if (pCurrentShader->IsInUse()) { qglColor3f (0.5,1,0.5); qglDisable (GL_TEXTURE_2D); qglBegin (GL_LINE_LOOP); qglVertex2f (x-3,y+3-FONT_HEIGHT); qglVertex2f (x-3,y-nHeight-3-FONT_HEIGHT); qglVertex2f (x+3+nWidth,y-nHeight-3-FONT_HEIGHT); qglVertex2f (x+3+nWidth,y+3-FONT_HEIGHT); qglEnd (); qglEnable (GL_TEXTURE_2D); } } // Draw the texture qglBindTexture (GL_TEXTURE_2D, q->texture_number); QE_CheckOpenGLForErrors(); qglColor3f (1,1,1); qglBegin (GL_QUADS); qglTexCoord2f (0,0); qglVertex2f (x,y-FONT_HEIGHT); qglTexCoord2f (1,0); qglVertex2f (x+nWidth,y-FONT_HEIGHT); qglTexCoord2f (1,1); qglVertex2f (x+nWidth,y-FONT_HEIGHT-nHeight); qglTexCoord2f (0,1); qglVertex2f (x,y-FONT_HEIGHT-nHeight); qglEnd (); // draw the texture name qglDisable (GL_TEXTURE_2D); qglColor3f (1,1,1); qglRasterPos2f (x, y-FONT_HEIGHT+2); // don't draw the directory name name = (char*)pCurrentShader->getName(); name += strlen(name); while(name != (char*)pCurrentShader->getName() && *(name-1) != '/' && *(name-1) != '\\') name--; gtk_glwidget_print_string(name); qglEnable (GL_TEXTURE_2D); } } g_qeglobals.d_texturewin.m_nTotalHeight = abs(y) + last_height + FONT_HEIGHT + 4; // reset the current texture qglBindTexture(GL_TEXTURE_2D, 0); qglFinish(); } //++timo seems we only know hard inits now.. //void Texture_Init (bool bHardInit) void Texture_Init() { g_qeglobals.d_qtextures = NULL; // initialize the qtexture map if (g_qeglobals.d_qtexmap) { Sys_FPrintf(SYS_ERR, "TODO: delete g_qeglobals.d_qtexmap in Texture_Init\n"); } g_qeglobals.d_qtexmap = g_hash_table_new (g_str_hash, g_str_equal); // initialize .. in some cases if no default texture / project loaded it crashes memset( &g_qeglobals.d_texturewin.texdef, 0, sizeof(g_qeglobals.d_texturewin.texdef) ); g_qeglobals.d_texturewin.texdef.SetName(SHADER_NOT_FOUND); g_qeglobals.d_texturewin.pShader = QERApp_Shader_ForName(SHADER_NOT_FOUND); } // FIXME TTimo this needs to move to the shader module along with l_shaderlist move // preload shader files that have been listed in shaderlist.txt void PreloadShaders() { GSList *lst = l_shaderfiles; Str shadername; while (lst) { shadername = g_pGameDescription->mShaderPath; shadername += (char*)lst->data; QERApp_LoadShaderFile(shadername.GetBuffer()); lst = lst->next; } } // TTimo: modified to expect the reletive path to the skin as input // will look into pak files if necessary // uses the shader code to load the texture Try_Texture_ForName // modified SkinInfo accordingly to store the qtexture_t and shader name (reletive version) // the .md3 have bundled filetype extension, but they don't fit with the actual data // ex: models/mapobjects/gargoyle.tga doesn't exist, but models/mapobjects/gargoyle.jpg can be used instead // so we remove the extension before load attempt int WINAPI Texture_LoadSkin(char *pName, int *pnWidth, int *pnHeight) { // byte *pic = NULL; // byte *pic32 = NULL; int nTex = -1; qtexture_t *qtex; SkinInfo *pInfo; const char *pCleanName; int nSize = g_lstSkinCache.GetSize(); pCleanName = QERApp_CleanTextureName( pName, false ); for (int i = 0; i < nSize; i++) { SkinInfo *pInfo = reinterpret_cast(g_lstSkinCache.GetAt(i)); if (pInfo) { if (stricmp(pCleanName, pInfo->m_strName) == 0) { return pInfo->m_nTextureBind; } } } // if the load is successfull, we get back a qtexture_t // we don't need to free it, it's in g_qeglobals.d_qtextures // NOTE: we need to free the SkinInfo though.. qtex = QERApp_Try_Texture_ForName( pCleanName ); if (qtex) { nTex = qtex->texture_number; pInfo = new SkinInfo(qtex->name, nTex, qtex); } else { pInfo = new SkinInfo(pCleanName, -1, NULL); } g_lstSkinCache.Add(pInfo); return nTex; } bool TexWnd::CheckFilter( const char* name ) { const char* buf = gtk_entry_get_text (GTK_ENTRY (m_pFilter)); if (strstr( name, buf ) != 0) return true; return false; } // ============================================================================= // static functions static void vertical_scroll (GtkWidget *widget, gpointer data) { ((TexWnd*)data)->OnVScroll (); } static void filter_changed (GtkWidget *widget, gpointer data) { CString str; str = gtk_entry_get_text (GTK_ENTRY (widget)); ((TexWnd*)data)->UpdateFilter (str); } // ============================================================================= // TexWnd class TexWnd::TexWnd() : GLWindow (FALSE) { m_pFilter = NULL; m_bNeedRange = true; } TexWnd::~TexWnd() { } void TexWnd::OnCreate () { if (!MakeCurrent ()) Error ("glMakeCurrent in TexWnd::OnCreate failed"); g_qeglobals_gui.d_texture = m_pWidget; g_nTextureOffset = 0; GtkAdjustment *vadjustment = gtk_range_get_adjustment (GTK_RANGE (g_qeglobals_gui.d_texture_scroll)); gtk_signal_connect (GTK_OBJECT (vadjustment), "value_changed", GTK_SIGNAL_FUNC (vertical_scroll), this); if (g_PrefsDlg.m_bTextureScrollbar) gtk_widget_show (g_qeglobals_gui.d_texture_scroll); else gtk_widget_hide (g_qeglobals_gui.d_texture_scroll); m_bNeedRange = true; gtk_signal_connect (GTK_OBJECT (m_pFilter), "changed", GTK_SIGNAL_FUNC (filter_changed), this); if (g_PrefsDlg.m_bTextureWindow) gtk_widget_show (m_pFilter); } void TexWnd::UpdateFilter(const char* pFilter) { g_bFilterEnabled = false; if (pFilter) { g_strFilter = pFilter; if (g_strFilter.GetLength() > 0) g_bFilterEnabled = true; QERApp_SortActiveShaders(); } Sys_UpdateWindows (W_TEXTURE); } void TexWnd::OnSize (int cx, int cy) { m_bNeedRange = true; } void TexWnd::OnExpose () { int nOld = g_qeglobals.d_texturewin.m_nTotalHeight; if (!MakeCurrent ()) { Sys_Printf("ERROR: glXMakeCurrent failed..\n "); Sys_Printf("Please restart Radiant if the Texture view is not working\n"); } else { QE_CheckOpenGLForErrors(); Texture_Draw (m_pWidget->allocation.width, m_pWidget->allocation.height - g_nTextureOffset); QE_CheckOpenGLForErrors(); SwapBuffers (); } if (g_PrefsDlg.m_bTextureScrollbar && (m_bNeedRange || g_qeglobals.d_texturewin.m_nTotalHeight != nOld)) { GtkAdjustment *vadjustment = gtk_range_get_adjustment (GTK_RANGE (g_qeglobals_gui.d_texture_scroll)); vadjustment->value = -g_qeglobals.d_texturewin.originy; vadjustment->page_size = m_pWidget->allocation.height; vadjustment->page_increment = m_pWidget->allocation.height/2; vadjustment->step_increment = 20; vadjustment->lower = 0; vadjustment->upper = g_qeglobals.d_texturewin.m_nTotalHeight; gtk_signal_emit_by_name (GTK_OBJECT (vadjustment), "changed"); m_bNeedRange = false; } } void TexWnd::OnLButtonDown (guint32 flags, int pointx, int pointy) { SetCapture (); Texture_MouseDown (pointx, pointy - g_nTextureOffset, flags); } void TexWnd::OnRButtonDown (guint32 flags, int pointx, int pointy) { SetCapture (); Texture_MouseDown (pointx, pointy - g_nTextureOffset, flags); } void TexWnd::OnMButtonDown (guint32 flags, int pointx, int pointy) { SetCapture (); Texture_MouseDown (pointx, pointy - g_nTextureOffset, flags); } void TexWnd::OnLButtonUp (guint32 flags, int pointx, int pointy) { ReleaseCapture (); // NOTE TTimo http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=23 DragDropTexture (flags, pointx, pointy); } void TexWnd::OnRButtonUp (guint32 flags, int pointx, int pointy) { ReleaseCapture (); } void TexWnd::OnMButtonUp (guint32 flags, int pointx, int pointy) { ReleaseCapture (); } void TexWnd::OnMouseMove (guint32 flags, int pointx, int pointy) { Texture_MouseMoved (pointx, pointy - g_nTextureOffset, flags); // if scrollbar is hidden, we don't seem to get an update if( !g_PrefsDlg.m_bTextureScrollbar ) RedrawWindow (); } void TexWnd::OnVScroll () { GtkAdjustment *vadjustment = gtk_range_get_adjustment (GTK_RANGE (g_qeglobals_gui.d_texture_scroll)); g_qeglobals.d_texturewin.originy = - (int)vadjustment->value; RedrawWindow (); } void TexWnd::UpdatePrefs() { if (g_PrefsDlg.m_bTextureWindow) gtk_widget_show (m_pFilter); else gtk_widget_hide (m_pFilter); if (g_PrefsDlg.m_bTextureScrollbar) gtk_widget_show (g_qeglobals_gui.d_texture_scroll); else gtk_widget_hide (g_qeglobals_gui.d_texture_scroll); m_bNeedRange = true; RedrawWindow (); } void TexWnd::FocusEdit() { if (GTK_WIDGET_VISIBLE (m_pFilter)) gtk_window_set_focus (GTK_WINDOW (g_pParentWnd->m_pWidget), m_pFilter); } void TexWnd::OnMouseWheel(bool bUp) { if (bUp) { if(g_qeglobals.d_texturewin.originy < 0) { g_qeglobals.d_texturewin.originy += g_PrefsDlg.m_nWheelInc; // clamp so we don't get jiggle if moved by less than scrollwheel increment if(g_qeglobals.d_texturewin.originy > 0) { g_qeglobals.d_texturewin.originy = 0; } } } else { if(g_qeglobals.d_texturewin.originy > (-g_qeglobals.d_texturewin.m_nTotalHeight + g_qeglobals.d_texturewin.height)) g_qeglobals.d_texturewin.originy -= g_PrefsDlg.m_nWheelInc; } GtkAdjustment *vadjustment = gtk_range_get_adjustment (GTK_RANGE (g_qeglobals_gui.d_texture_scroll)); gtk_adjustment_set_value (vadjustment, abs(g_qeglobals.d_texturewin.originy)); RedrawWindow(); } // NOTE TTimo // http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=23 void TexWnd::DragDropTexture (guint32 flags, int pointx, int pointy) { // This gets called from leftmouse up event. We see if the mouseup is above // the camwindow. If this is the case do a trace for a surface. If we hit a // surface, texture it with the current texture. int m_ptXcheck, m_ptYcheck; int m_ptX, m_ptY; GtkWidget *widget; gint x, y; vec3_t dir; float f, r, u; int i; // we only want to catch a plain mouseevent if (flags) return; // see if we are above the camwindow Sys_GetCursorPos (&m_ptX, &m_ptY); if (g_pParentWnd->CurrentStyle() == MainFrame::eFloating) widget = g_pParentWnd->GetCamWnd()->m_pParent; else widget = g_pParentWnd->GetCamWnd()->GetWidget(); get_window_pos (widget, &x, &y); if (m_ptX < x || m_ptY < y || m_ptX > x + widget->allocation.width || m_ptY > y + widget->allocation.height) return; // check if the camwindow isn't being partially hidden by another window at this point // http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=187 m_ptXcheck = m_ptX; m_ptYcheck = m_ptY; if( g_pParentWnd->GetCamWnd()->GetWidget()->window != gdk_window_at_pointer( &m_ptXcheck, &m_ptYcheck ) ) return; // calc ray direction x = m_ptX - x; y = g_pParentWnd->GetCamWnd()->Camera()->height - 1 - (m_ptY - y); u = (float)( y - ( g_pParentWnd->GetCamWnd()->Camera()->height * .5f ) ) / ( g_pParentWnd->GetCamWnd()->Camera()->height * .5f ); r = (float)( x - ( g_pParentWnd->GetCamWnd()->Camera()->width * .5f ) ) / ( g_pParentWnd->GetCamWnd()->Camera()->width * .5f ); f = 1; for (i=0 ; i<3 ; i++) dir[i] = g_pParentWnd->GetCamWnd()->Camera()->vpn[i] * f + g_pParentWnd->GetCamWnd()->Camera()->vright[i] * r + g_pParentWnd->GetCamWnd()->Camera()->vup[i] * u; VectorNormalize (dir, dir); // do a trace for a surface trace_t t; t = Test_Ray (g_pParentWnd->GetCamWnd()->Camera()->origin, dir, SF_SINGLEFACE); if (t.brush) { texdef_t tex; brushprimit_texdef_t brushprimit_tex; memset (&tex, 0, sizeof(tex)); memset (&brushprimit_tex, 0, sizeof(brushprimit_tex)); if (g_qeglobals.m_bBrushPrimitMode) { // brushprimit fitted to a 2x2 texture brushprimit_tex.coords[0][0] = 1.0f; brushprimit_tex.coords[1][1] = 1.0f; } else { tex.scale[0] = g_pGameDescription->mTextureDefaultScale; tex.scale[1] = g_pGameDescription->mTextureDefaultScale; } tex.flags = g_qeglobals.d_texturewin.texdef.flags; tex.value = g_qeglobals.d_texturewin.texdef.value; tex.contents = g_qeglobals.d_texturewin.texdef.contents; // TTimo - shader code cleanup // texdef.name is the name of the shader, not the name of the actual texture file tex.SetName(g_qeglobals.d_texturewin.texdef.GetName()); Undo_Start("set face textures"); Undo_AddBrush(t.brush); SetFaceTexdef (t.face, &tex, &brushprimit_tex, false, NULL ); Brush_Build(t.brush, false); Undo_EndBrush(t.brush); Undo_End(); Sys_UpdateWindows (W_CAMERA); g_pParentWnd->OnTimer (); } }