]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - radiant/texwindow.cpp
* Tremulous patch by Ingar
[xonotic/netradiant.git] / radiant / texwindow.cpp
1 /*
2 Copyright (C) 1999-2007 id Software, Inc. and contributors.
3 For a list of contributors, see the accompanying CONTRIBUTORS file.
4
5 This file is part of GtkRadiant.
6
7 GtkRadiant is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 GtkRadiant is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with GtkRadiant; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20 */
21
22 //
23 // Texture Window
24 //
25 // Leonardo Zide (leo@lokigames.com)
26 //
27
28 /*!\todo
29 Clean up texture menu.
30 - Remove all global variables and use some objects instead.
31 - Create an interface for a plugin to add texture menu items.
32 - Make sure the interface is not dependent on gtk.
33 */
34
35 #ifdef _WIN32
36 //#include <gdk/win32/gdkwin32.h>
37 #include <gdk/gdkwin32.h>
38 #endif
39 #if defined (__linux__) || defined (__APPLE__)
40 #include <gdk/gdkx.h>
41 #include <dirent.h>
42 #endif
43 #include <gtk/gtk.h>
44 #include <assert.h>
45 #include <sys/stat.h>
46 #include "stdafx.h"
47 #include "texwindow.h"
48 #include "str.h"
49 #include "missing.h"
50 #include "texmanip.h"
51
52 #define TYP_MIPTEX      68
53 static unsigned tex_palette[256];
54
55 #define FONT_HEIGHT     10
56
57 //int           texture_mode = GL_NEAREST;
58 //int           texture_mode = GL_NEAREST_MIPMAP_NEAREST;
59 //int           texture_mode = GL_NEAREST_MIPMAP_LINEAR;
60 //int           texture_mode = GL_LINEAR;
61 //int           texture_mode = GL_LINEAR_MIPMAP_NEAREST;
62 int             texture_mode = GL_LINEAR_MIPMAP_LINEAR;
63
64 int g_nTextureOffset = 0;
65
66 // current active texture directory
67 //++timo FIXME: I'm not sure this is used anymore
68 char            texture_directory[128];
69 // if true, the texture window will only display in-use shaders
70 // if false, all the shaders in memory are displayed
71 qboolean g_bShowAllShaders;
72
73 bool g_bFilterEnabled = false;
74 CString g_strFilter;
75
76 // texture layout functions
77 // TTimo: now based on shaders
78 int     nActiveShadersCount;
79 int     nCurrentShader;
80 IShader*  pCurrentShader;
81 qtexture_t  *current_texture = NULL;
82 int     current_x, current_y, current_row;
83
84 // globals for textures
85 int     texture_nummenus;
86 char    texture_menunames[MAX_TEXTUREDIRS][128];
87
88 // the list of scripts/*.shader files we need to work with
89 // those are listed in shaderlist file
90 // FIXME TTimo I get the feeling that those would need to move to the shaders module
91 //   for now it's still more simple to just keep it here
92 GSList *l_shaderfiles = NULL;
93
94 void SelectTexture (int mx, int my, bool bShift, bool bFitScale=false);
95
96 void  Texture_MouseDown (int x, int y, int buttons);
97 void  Texture_MouseMoved (int x, int y, int buttons);
98
99 CPtrArray g_lstSkinCache;
100
101 // TTimo: modifed to add a qtexture_t, Texture_LoadSkin loads using the shader API / QERApp_TryTexture_ForName
102 // m_strName is a copy of qtex->name
103 struct SkinInfo
104 {
105   CString m_strName;
106   int m_nTextureBind;
107   qtexture_t *m_qtex;
108   SkinInfo(const char *pName, int n, qtexture_t *qtex)
109   {
110     m_strName = pName;
111     m_nTextureBind = n;
112     m_qtex = qtex;
113   };
114   SkinInfo(){};
115 };
116
117 // =============================================================================
118 // global functions
119
120 // gets active texture extension
121 //
122 // FIXME: fix this to be generic from project file
123 //
124 int GetTextureExtensionCount()
125 {
126   // hardcoded hack for png support
127   if (g_pGameDescription->mGameFile == "sof2.game")
128     return 3;
129   else
130     return 2;
131 }
132
133 const char* GetTextureExtension(int nIndex)
134 {
135   switch(nIndex)
136   {
137     case 0:
138       return "tga";
139       break;
140     case 1:
141       return "jpg";
142       break;
143     case 2:
144       return "png";
145       break;
146     default:
147       return NULL;
148   }
149 }
150
151 /*
152 ==============
153 Texture_InitPalette
154 ==============
155 */
156 void Texture_InitPalette (byte *pal)
157 {
158   int   r,g,b;
159   int   i;
160   int   inf;
161   byte  gammatable[256];
162   float gamma;
163
164   gamma = g_qeglobals.d_savedinfo.fGamma;
165
166   if (gamma == 1.0)
167   {
168     for (i=0 ; i<256 ; i++)
169       gammatable[i] = i;
170   } else
171   {
172     for (i=0 ; i<256 ; i++)
173     {
174       inf = (int)( 255.0f * pow( ( i + 0.5f ) / 255.5f , gamma ) + 0.5f );
175       if (inf < 0)
176         inf = 0;
177       if (inf > 255)
178         inf = 255;
179       gammatable[i] = inf;
180     }
181   }
182
183   for (i=0 ; i<256 ; i++)
184   {
185     r = gammatable[pal[0]];
186     g = gammatable[pal[1]];
187     b = gammatable[pal[2]];
188     pal += 3;
189
190     //v = (r<<24) + (g<<16) + (b<<8) + 255;
191     //v = BigLong (v);
192
193     //tex_palette[i] = v;
194     tex_palette[i*3+0] = r;
195     tex_palette[i*3+1] = g;
196     tex_palette[i*3+2] = b;
197   }
198 }
199
200 void SetTexParameters (void)
201 {
202   qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, texture_mode );
203
204   switch ( texture_mode )
205   {
206   case GL_NEAREST:
207   case GL_NEAREST_MIPMAP_NEAREST:
208   case GL_NEAREST_MIPMAP_LINEAR:
209     qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
210     break;
211   case GL_LINEAR:
212   case GL_LINEAR_MIPMAP_NEAREST:
213   case GL_LINEAR_MIPMAP_LINEAR:
214     qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
215     break;
216   }
217 }
218
219 /*
220 ============
221 Texture_SetMode
222 ============
223 */
224 void Texture_SetMode(int iMenu)
225 {
226   int iMode;
227   qboolean texturing = true;
228   gpointer item = NULL;
229
230   switch (iMenu)
231   {
232   case ID_VIEW_NEAREST:
233     item = g_object_get_data (G_OBJECT (g_pParentWnd->m_pWidget), "menu_view_nearest");
234     iMode = GL_NEAREST;
235     break;
236   case ID_VIEW_NEARESTMIPMAP:
237     item = g_object_get_data (G_OBJECT (g_pParentWnd->m_pWidget), "menu_view_nearestmipmap");
238     iMode = GL_NEAREST_MIPMAP_NEAREST;
239     break;
240   case ID_VIEW_LINEAR:
241     item = g_object_get_data (G_OBJECT (g_pParentWnd->m_pWidget), "menu_view_linear");
242     iMode = GL_LINEAR;
243     break;
244   case ID_VIEW_BILINEAR:
245     item = g_object_get_data (G_OBJECT (g_pParentWnd->m_pWidget), "menu_view_bilinear");
246     iMode = GL_NEAREST_MIPMAP_LINEAR;
247     break;
248   case ID_VIEW_BILINEARMIPMAP:
249     item = g_object_get_data (G_OBJECT (g_pParentWnd->m_pWidget), "menu_view_bilinearmipmap");
250     iMode = GL_LINEAR_MIPMAP_NEAREST;
251     break;
252   case ID_VIEW_TRILINEAR:
253     item = g_object_get_data (G_OBJECT (g_pParentWnd->m_pWidget), "menu_view_trilinear");
254     iMode = GL_LINEAR_MIPMAP_LINEAR;
255     break;
256   case ID_TEXTURES_WIREFRAME:
257     item = g_object_get_data (G_OBJECT (g_pParentWnd->m_pWidget), "menu_view_wireframe");
258     iMode = -1;
259     texturing = false;
260     break;
261   case ID_TEXTURES_FLATSHADE:
262     item = g_object_get_data (G_OBJECT (g_pParentWnd->m_pWidget), "menu_view_flatshade");
263     iMode = -1;
264     texturing = false;
265     break;
266   default:
267     return;
268   }
269
270   g_qeglobals.d_savedinfo.iTexMenu = iMenu;
271   // NOTE: texture_mode is a GLenum used directly in glTexParameter
272   if(iMode!=-1) texture_mode = iMode;
273
274   g_bIgnoreCommands++;
275   if (item != NULL)
276     gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), TRUE);
277   g_bIgnoreCommands--;
278
279   if (texturing)
280     SetTexParameters ();
281
282   if ( !texturing && iMenu == ID_TEXTURES_WIREFRAME)
283   {
284     g_pParentWnd->GetCamWnd()->Camera()->draw_mode = cd_wire;
285     Map_BuildBrushData();
286     Sys_UpdateWindows (W_ALL);
287     return;
288   } else if ( !texturing && iMenu == ID_TEXTURES_FLATSHADE)
289   {
290     g_pParentWnd->GetCamWnd()->Camera()->draw_mode = cd_solid;
291     Map_BuildBrushData();
292     Sys_UpdateWindows (W_ALL);
293     return;
294   }
295
296   for (qtexture_t *q = g_qeglobals.d_qtextures; q; q = q->next)
297   {
298     qglBindTexture (GL_TEXTURE_2D, q->texture_number);
299     SetTexParameters ();
300   }
301
302   // select the default texture
303   qglBindTexture( GL_TEXTURE_2D, 0 );
304
305   qglFinish();
306
307   if (g_pParentWnd->GetCamWnd()->Camera()->draw_mode != cd_texture)
308   {
309     g_pParentWnd->GetCamWnd()->Camera()->draw_mode = cd_texture;
310     Map_BuildBrushData();
311   }
312
313   Sys_UpdateWindows (W_ALL);
314 }
315
316 /*!
317 gamma correction stuff
318 took out of QERApp_LoadTextureRGBA for clarity
319 */
320 byte g_gammatable[256];
321 void ResampleGamma(float fGamma)
322 {
323   int i,inf;
324   if (fGamma == 1.0)
325   {
326     for (i = 0; i < 256; i++)
327       g_gammatable[i] = i;
328   } else
329   {
330     for (i = 0; i < 256; i++)
331     {
332       inf = (int)( 255.0f * pow( (i + 0.5f) / 255.5f , fGamma ) + 0.5f );
333       if (inf < 0)
334         inf = 0;
335       if (inf > 255)
336         inf = 255;
337       g_gammatable[i] = inf;
338     }
339   }
340 }
341
342 /*!
343 this function does the actual processing of raw RGBA data into a GL texture
344 it will also generate the mipmaps
345 it looks like pPixels nWidth nHeight are the only relevant parameters
346 */
347 qtexture_t *QERApp_LoadTextureRGBA(unsigned char* pPixels, int nWidth, int nHeight)
348 {
349   static float fGamma = -1;
350   float total[3];
351   byte  *outpixels = NULL;
352   int   i, j, resampled, width2, height2, width3, height3;
353   int   max_tex_size = 0, mip = 0;
354   int   nCount = nWidth * nHeight;
355
356   if (fGamma != g_qeglobals.d_savedinfo.fGamma)
357   {
358     fGamma = g_qeglobals.d_savedinfo.fGamma;
359     ResampleGamma(fGamma);
360   }
361
362   qglGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_tex_size);
363   if (!max_tex_size)
364     max_tex_size = 1024;
365
366   qtexture_t *q = (qtexture_t*)g_malloc(sizeof(*q));
367   q->width = nWidth;
368   q->height = nHeight;
369
370   total[0] = total[1] = total[2] = 0.0f;
371
372   // resample texture gamma according to user settings
373   for (i = 0; i < (nCount * 4); i += 4)
374   {
375     for (j = 0; j < 3; j++)
376     {
377       total[j] += (pPixels + i)[j];
378       byte b = (pPixels + i)[j];
379       (pPixels + i)[j] = g_gammatable[b];
380     }
381   }
382
383   q->color[0] = total[0] / (nCount * 255);
384   q->color[1] = total[1] / (nCount * 255);
385   q->color[2] = total[2] / (nCount * 255);
386
387   qglGenTextures (1, &q->texture_number);
388
389   qglBindTexture( GL_TEXTURE_2D, q->texture_number );
390
391   SetTexParameters();
392
393   width2 = 1; while (width2 < nWidth) width2 <<= 1;
394   height2 = 1; while (height2 < nHeight) height2 <<= 1;
395
396   width3 = width2;
397   height3 = height2;
398   while (width3 > max_tex_size) width3 >>= 1;
399   while (height3 > max_tex_size) height3 >>= 1;
400   if (width3 < 1) width3 = 1;
401   if (height3 < 1) height3 = 1;
402
403   if (!(width2 == nWidth && height2 == nHeight)) {
404     resampled = 1;
405     outpixels = (byte *)malloc(width2 * height2 * 4);
406     R_ResampleTexture(pPixels, nWidth, nHeight, outpixels, width2, height2, 4);
407   } else {
408     resampled = 0;
409     outpixels = pPixels;
410   }
411
412   while (width2 > width3 || height2 > height3)
413   {
414     GL_MipReduce(outpixels, outpixels, width2, height2, width3, height3);
415
416     if (width2 > width3)
417       width2 >>= 1;
418     if (height2 > height3)
419       height2 >>= 1;
420   }
421
422   qglTexImage2D(GL_TEXTURE_2D, mip++, g_qeglobals.texture_components, width2, height2, 0, GL_RGBA, GL_UNSIGNED_BYTE, outpixels);
423   while (width2 > 1 || height2 > 1)
424   {
425     GL_MipReduce(outpixels, outpixels, width2, height2, 1, 1);
426
427     if (width2 > 1)
428       width2 >>= 1;
429     if (height2 > 1)
430       height2 >>= 1;
431
432     qglTexImage2D(GL_TEXTURE_2D, mip++, g_qeglobals.texture_components, width2, height2, 0, GL_RGBA, GL_UNSIGNED_BYTE, outpixels);
433   }
434
435   qglBindTexture(GL_TEXTURE_2D, 0);
436   if (resampled)
437     free(outpixels);
438
439   return q;
440 }
441
442 /*
443 ==================
444 DumpUnreferencedShaders
445 usefull function: dumps the list of .shader files that are not referenced to the console
446 ==================
447 */
448 void DumpUnreferencedShaders()
449 {
450   GSList *lst, *sh, *files;
451   bool bFound = false;
452
453   files = vfsGetFileList ("scripts", "shader");
454   for (lst = files; lst; lst = lst->next)
455   {
456     bool listed = false;
457
458     for (sh = l_shaderfiles; sh != NULL; sh = g_slist_next (sh))
459       if (!strcmp ((char*)sh->data, (char*)lst->data))
460       {
461         listed = true;
462         break;
463       }
464
465     if (!listed)
466     {
467       if (!bFound)
468       {
469         bFound = true;
470         Sys_FPrintf (SYS_WRN, "Following shader files are not referenced in shaderlist.txt:\n");
471       }
472       Sys_FPrintf (SYS_WRN, "%s\n", (char*)lst->data);
473     }
474   }
475
476   vfsClearFileDirList (&files);
477 }
478
479 /*
480 ==================
481 BuildShaderList
482 build a CStringList of shader names
483 ==================
484 */
485 void BuildShaderList()
486 {
487   int count;
488   char filename[1024];
489   char *pBuff;
490   char dirstring[NAME_MAX];
491   int nLen;
492   if (l_shaderfiles!=NULL)
493   {
494     g_slist_free(l_shaderfiles);
495     l_shaderfiles = NULL;
496   }
497
498   if (g_pGameDescription->mGameFile != "hl.game")
499   {
500     strcpy(filename, g_pGameDescription->mShaderlist.GetBuffer());
501     count = vfsGetFileCount(filename, 0 );
502     if (count==0)
503     {
504       Sys_FPrintf(SYS_ERR, "Couldn't find '%s'\n", g_pGameDescription->mShaderlist.GetBuffer());
505       return;
506     }
507     // NOTE TTimo we use vfsGetFullPath solely to get the full path of the shader list we are gonna load
508     //   but we actually send the relative path to vfsLoadFile
509     //   so let's hope there is no disparity between the two functions
510     if (!vfsGetFullPath(filename, 0, 0))
511     {
512       Sys_FPrintf(SYS_ERR, "Couldn't find full path for '%s'\n", g_pGameDescription->mShaderlist.GetBuffer());
513       Sys_FPrintf(SYS_ERR, "did you hit bug http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=130 ?\n");
514       return;
515     }
516     Sys_Printf("Parsing shader files from %s\n", vfsGetFullPath(filename, 0, 0));
517     nLen = vfsLoadFile (filename, reinterpret_cast<void**>(&pBuff), 0);
518     if (nLen > 0)
519     {
520       StartTokenParsing(pBuff);
521       nLen = 0;
522       while (GetToken(true))
523       {
524         GSList *tmp;
525         bool found = false;
526
527         // each token should be a shader filename
528         sprintf(dirstring, "%s.shader", token);
529
530         for (tmp = l_shaderfiles; tmp != NULL; tmp = tmp->next)
531         {
532           if (!strcmp (dirstring, (char*)tmp->data))
533           {
534             found = true;
535             Sys_FPrintf(SYS_WRN, "duplicate entry \"%s\" in shaderlist.txt\n", (char*)tmp->data);
536             break;
537           }
538         }
539
540         if (!found)
541         {
542           l_shaderfiles = g_slist_append (l_shaderfiles, strdup (dirstring));
543           nLen++;
544         }
545       }
546       g_free(pBuff);
547     }
548   }
549 }
550
551 /*
552 ==================
553 FillTextureMenu
554
555 ==================
556 */
557 void ClearGSList (GSList* lst)
558 {
559   GSList *p = lst;
560   while (p)
561   {
562     free (p->data);
563     p = g_slist_remove (p, p->data);
564   }
565 }
566
567 void FillTextureMenu (GSList** pArray)
568 {
569   GtkWidget *menu, *sep, *item; // point to the Textures GtkMenu and to the last separator
570   GList *lst;
571   GSList *texdirs = NULL;
572   GSList *texdirs_tmp = NULL;
573   GSList *p;
574   char dirRoot[NAME_MAX];
575
576   // delete everything
577   menu = GTK_WIDGET (g_object_get_data (G_OBJECT (g_qeglobals_gui.d_main_window), "menu_textures"));
578   sep = GTK_WIDGET (g_object_get_data (G_OBJECT (g_qeglobals_gui.d_main_window), "menu_textures_separator"));
579   lst = g_list_find (gtk_container_children (GTK_CONTAINER (menu)), sep);
580   while (lst->next)
581   {
582     // these delete functions are recursive, it's gonna free all submenus
583     gtk_widget_destroy (GTK_WIDGET (lst->next->data));
584     // lst is no longer relevant, need to get it again
585     lst = g_list_find (gtk_container_children (GTK_CONTAINER (menu)), sep);
586   }
587
588   texture_nummenus = 0;
589
590   // add everything
591   if (!g_qeglobals.d_project_entity)
592     return;
593
594   // scan texture dirs and pak files only if not restricting to shaderlist
595   if (!g_PrefsDlg.m_bTexturesShaderlistOnly)
596   {
597     texdirs_tmp = vfsGetDirList ("textures/");
598     for (p=texdirs_tmp; p; p=g_slist_next(p))
599     {
600       // Hydra: erm, this didn't used to do anything except leak memory...
601       // For Halflife support this is required to work however.
602       // g_slist_append(texdirs, p->data);
603       texdirs = g_slist_append(texdirs, strdup((char *)p->data));
604     }
605     vfsClearFileDirList (&texdirs_tmp);
606   }
607
608   // scan the shaders in shaderlist.txt
609   BuildShaderList ();
610   PreloadShaders ();
611   DumpUnreferencedShaders ();
612   while (l_shaderfiles != NULL)
613   {
614     char shaderfile[PATH_MAX];
615     gboolean found = FALSE;
616
617     ExtractFileName ((char*)l_shaderfiles->data, shaderfile);
618     StripExtension (shaderfile);
619     g_strdown (shaderfile);
620
621     for (GSList *tmp = texdirs; tmp; tmp = g_slist_next (tmp))
622       if (!strcasecmp ((char*)tmp->data, shaderfile))
623       {
624               found = TRUE;
625               break;
626       }
627
628     if (!found)
629       texdirs = g_slist_prepend (texdirs, strdup (shaderfile));
630
631     free (l_shaderfiles->data);
632     l_shaderfiles = g_slist_remove (l_shaderfiles, l_shaderfiles->data);
633   }
634
635   // sort the list
636   texdirs = g_slist_sort (texdirs, (GCompareFunc)strcmp);
637
638   GSList *temp = texdirs;
639   while (temp)
640   {
641     char* ptr = strchr ((char*)temp->data, '_');
642
643     // do we shrink the menus?
644     if (ptr != NULL)
645     {
646       // extract the root
647       strcpy (dirRoot, (char*)temp->data);
648       dirRoot[ptr - (char*)temp->data + 1] = 0;
649
650       // we shrink only if we have at least two things to shrink :-)
651       if (temp->next && (strstr ((char*)temp->next->data, dirRoot) == (char*)temp->next->data))
652       {
653               GtkWidget *pSubMenu = gtk_menu_new ();
654         GtkWidget *pSubMenuRef = pSubMenu;
655               // keep going...
656               do
657               {
658                 item = gtk_menu_item_new_with_label ((char*)temp->data);
659                 gtk_widget_show (item);
660           CheckMenuSplitting (pSubMenu);
661                 gtk_container_add (GTK_CONTAINER (pSubMenu), item);
662                 gtk_signal_connect (GTK_OBJECT (item), "activate", GTK_SIGNAL_FUNC (HandleCommand),
663                                     GINT_TO_POINTER (CMD_TEXTUREWAD+texture_nummenus));
664
665                 strcpy (texture_menunames[texture_nummenus], (char*)temp->data);
666                 strcat (texture_menunames[texture_nummenus], "/");
667                 if (pArray)
668                   *pArray = g_slist_append (*pArray, strdup ((char*)temp->data));
669                 if (++texture_nummenus == MAX_TEXTUREDIRS)
670                 {
671                   Sys_Printf("WARNING: max texture directories count has been reached!\n");
672                   // push submenu and get out
673                   item = gtk_menu_item_new_with_label (dirRoot);
674                   gtk_widget_show (item);
675                   gtk_container_add (GTK_CONTAINER (menu), item);
676                   gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), pSubMenu);
677                   ClearGSList (texdirs);
678                   return;
679                 }
680                 temp = temp->next;
681               }
682         while (temp && (strstr((char*)temp->data, dirRoot)==temp->data));
683
684         ptr = strchr (dirRoot, '_');
685         *ptr = 0;
686               item = gtk_menu_item_new_with_label (dirRoot);
687               gtk_widget_show (item);
688               CheckMenuSplitting (menu);
689               gtk_container_add (GTK_CONTAINER (menu), item);
690         gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), pSubMenuRef);
691               continue;
692       }
693     }
694
695     item = gtk_menu_item_new_with_label ((char*)temp->data);
696     gtk_widget_show (item);
697     CheckMenuSplitting (menu);
698     gtk_container_add (GTK_CONTAINER (menu), item);
699     gtk_signal_connect (GTK_OBJECT (item), "activate", GTK_SIGNAL_FUNC (HandleCommand),
700                         GINT_TO_POINTER (CMD_TEXTUREWAD+texture_nummenus));
701
702     strcpy (texture_menunames[texture_nummenus], (char*)temp->data);
703     strcat (texture_menunames[texture_nummenus], "/");
704     if (pArray)
705       *pArray = g_slist_append (*pArray, strdup ((char*)temp->data));
706     if (++texture_nummenus == MAX_TEXTUREDIRS)
707     {
708       Sys_Printf("WARNING: max texture directories count has been reached!\n");
709       ClearGSList (texdirs);
710       return;
711     }
712
713     temp = temp->next;
714   }
715   ClearGSList (texdirs);
716 }
717
718 /*
719 ==============
720 Texture_ShowDirectory
721 relies on texture_directory global for the directory to use
722 called by
723   void Texture_ShowDirectory (int menunum, bool bLinked)
724   void  Texture_ShowDirectory (char* pPath, bool bLinked)
725 1) Load the shaders for the given directory
726 2) Scan the remaining texture, load them and assign them a default shader (the "noshader" shader)
727 NOTE: when writing a texture plugin, or some texture extensions, this function may need to be overriden, and made
728   available through the IShaders interface
729 NOTE: for texture window layout:
730   all shaders are stored with alphabetical order after load
731   previously loaded and displayed stuff is hidden, only in-use and newly loaded is shown
732   ( the GL textures are not flushed though)
733 ==============
734 */
735 void Texture_ShowDirectory ()
736 {
737   char  name[1024];
738   char  dirstring[1024];
739   CString strTemp;
740   int shaders_count = 0;
741   int textures_count = 0;
742   GSList *files = NULL, *temp;
743
744   g_bScreenUpdates = false;
745
746   // refresh the in-use textures: that will clear the IsDisplayed flag on unused stuff
747   // and leave it on in-use so they'll still be displayed
748   Texture_ShowInuse();
749   // and textures loaded in the following lines will be displayed as well...
750   // NOTE: shaders that are not in use but have been loaded previously are still in memory. But they don't get displayed.
751
752   g_qeglobals.d_texturewin.originy = 0;
753   // load texture_directory.shader
754   // NOTE: because of above call to Texture_ClearInuse, g_ActiveShaders will have the newly loaded shaders only
755   // we'll use that later to check if textures have a shader associated or not
756   // 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
757   // NOTE: QERApp_LoadShadersFromDir has two criterions for loading a shader:
758   //   the shaderfile is texture_directory (like "museum" will load everything in museum.shader)
759   //   the shader name contains texture_directory (like "base_floor" will load museum.shader::base_floor/concfloor_rain)
760   shaders_count = QERApp_LoadShadersFromDir(texture_directory);
761   // load remaining texture files
762   // if a texture is already in use to represent a shader, ignore it
763
764   // need this function "GSList *lst SynapseServer::GetMinorList(char *major_name);"
765
766   sprintf (dirstring, "textures/%s", texture_directory);
767   g_ImageManager.BeginExtensionsScan();
768   const char* ext;
769   while((ext=g_ImageManager.GetNextExtension()) != NULL)
770   {
771     files = g_slist_concat(files, vfsGetFileList (dirstring, ext));
772   }
773
774   for (temp = files; temp; temp = temp->next)
775   {
776     sprintf(name, "%s%s", texture_directory, (char*)temp->data);
777
778       StripExtension (name);
779       strTemp = name;
780       strTemp.MakeLower();
781
782     if (strTemp.Find(".specular") >= 0 ||
783          strTemp.Find(".glow") >= 0 ||
784          strTemp.Find(".bump") >= 0 ||
785          strTemp.Find(".diffuse") >= 0 ||
786          strTemp.Find(".blend") >= 0 ||
787                strTemp.Find(".alpha") >= 0)
788          continue;
789
790     // avoid ever loading a texture name with spaces
791     if (strTemp.Find(" ") >= 0)
792     {
793       Sys_FPrintf(SYS_WRN, "WARNING: Skipping texture name with spaces [%s]\n", strTemp.GetBuffer());
794       continue;
795     }
796
797     // build a texture name that fits the conventions for qtexture_t::name
798     char stdName[1024];
799     sprintf( stdName, "textures/%s", name );
800     // check if this texture doesn't have a shader
801     if (!QERApp_ActiveShader_ForTextureName( stdName ))
802     {
803       QERApp_CreateShader_ForTextureName (stdName);
804       textures_count++;
805     }
806   }
807
808   Sys_Printf("Loaded %d shaders and created default shader for %d orphan textures.\n",
809              shaders_count, textures_count );
810
811   vfsClearFileDirList (&files);
812
813   // sort for displaying
814   QERApp_SortActiveShaders();
815
816   sprintf (name, "Textures: %s", texture_directory);
817   gtk_window_set_title (GTK_WINDOW (g_qeglobals_gui.d_entity), name);
818
819   // select the first texture in the list
820   if (!g_qeglobals.d_texturewin.texdef.GetName()[0])
821     SelectTexture (16, g_qeglobals.d_texturewin.height -16, false);
822
823   g_bScreenUpdates = true;
824
825   Sys_UpdateWindows (W_TEXTURE);
826 }
827
828 /*
829 ==============
830 Texture_ShowDirectory
831 1) Load the shaders for the given directory
832 2) Scan the remaining texture, load them and assign them a default shader (the "noshader" shader)
833 NOTE: when writing a texture plugin, or some texture extensions, this function may need to be overriden, and made
834   available through the IShaders interface
835 ==============
836 */
837 void Texture_ShowDirectory (int menunum)
838 {
839   strcpy (texture_directory, texture_menunames[menunum-CMD_TEXTUREWAD]);
840   Texture_ShowDirectory();
841 }
842
843 // scroll origin so the current texture is completely on screen
844 // if current texture is not displayed, nothing is changed
845 void Texture_ResetPosition()
846 {
847   qtexture_t  *q;
848   int  x,y;
849
850   //this shouldn't ever happen, we startup with notex
851   if (!g_qeglobals.d_texturewin.texdef.GetName()[0]) {
852     return;
853   }
854
855   // otherwise position with current texture shown
856   // this used to be in Texture_SetTexture
857   Texture_StartPos ();
858   while (1)
859   {
860     // NOTE: return value is == pCurrentShader and pCurrentShader->getTexture == current_texture
861     Texture_NextPos (&x, &y);
862     q = current_texture;
863     // if the current texture never found (because // 'show shaders' is off,
864     // for example), do nothing
865     if (!q)
866       break;
867
868     int nHeight = (int)(q->height * ((float)g_PrefsDlg.m_nTextureScale / 100));
869     // we have found when texdef->name and the shader name match
870     // NOTE: as everywhere else for our comparisons, we are not case sensitive
871     if (!strcmpi( g_qeglobals.d_texturewin.texdef.GetName(), pCurrentShader->getName() ))
872     {
873       // take care of calls before initialized
874       if ( !g_qeglobals.d_texturewin.height) {
875         g_qeglobals.d_texturewin.originy = 0;
876         break;
877       }
878       // if the bottom of our selected texture will fit with origin 0, use that
879       // to prevent scrolling uglyness (stuff scrolled off screen when
880       // everything would fit)
881       if ( -(y -nHeight-2*FONT_HEIGHT) <  g_qeglobals.d_texturewin.height) {
882         g_qeglobals.d_texturewin.originy = 0;
883         break;
884       }
885       // if current is off the top of the window, move it to the top
886       if (y > g_qeglobals.d_texturewin.originy)
887       {
888         g_qeglobals.d_texturewin.originy = y;
889         break;
890       }
891
892       // if current is off the bottom, put it on the bottom
893       if (y-nHeight-2*FONT_HEIGHT < g_qeglobals.d_texturewin.originy-g_qeglobals.d_texturewin.height)
894       {
895         g_qeglobals.d_texturewin.originy = y-nHeight-2*FONT_HEIGHT+g_qeglobals.d_texturewin.height;
896         break;
897       }
898       // if we made it here, it should already be in view
899       break;
900     }
901   }
902   Sys_UpdateWindows (W_TEXTURE);
903 }
904
905 /*
906 ==============
907 Texture_ShowAll
908 will set the IsDisplayed flag on all the active shaders, so we see everything that's currently in memory
909 ==============
910 */
911 void Texture_ShowAll()
912 {
913   char name[1024];
914
915 #ifdef _DEBUG
916   if (g_bShowAllShaders)
917     Sys_Printf("WARNING: already showing all shaders\n");
918 #endif
919   QERApp_ActiveShaders_SetDisplayed(true);
920   g_bShowAllShaders = true;
921   // put some information in the texture window title?
922   sprintf (name, "Textures: in use");
923   gtk_window_set_title (GTK_WINDOW (g_qeglobals_gui.d_entity), name);
924   Sys_UpdateWindows (W_TEXTURE);
925 }
926
927 /*
928 ==============
929 Texture_ShowInuse
930 clear all IsDisplayed flags
931 scan the map, set IsInUse (will set IsDisplayed on the way)
932 NOTE: don't sort the textures, don't update the windows (it's used in several contexts, not always necessary to do either)
933 ==============
934 */
935 void WINAPI Texture_ShowInuse (void)
936 {
937   face_t  *f;
938   brush_t *b;
939   char  name[1024];
940
941   g_qeglobals.d_texturewin.originy = 0;
942
943   // purge
944   QERApp_ActiveShaders_SetDisplayed(false);
945   // scan and only display in-use stuff
946   Sys_Status("Selecting active textures", 0);
947
948   for (b=active_brushes.next ; b != NULL && b != &active_brushes ; b=b->next)
949   {
950     if (b->patchBrush)
951     {
952       b->pPatch->pShader->SetInUse(true);
953     } else
954     {
955       for (f=b->brush_faces ; f ; f=f->next)
956       {
957         f->pShader->SetInUse(true);
958       }
959     }
960   }
961   for (b=selected_brushes.next ; b != NULL && b != &selected_brushes ; b=b->next)
962   {
963     if (b->patchBrush)
964     {
965       b->pPatch->pShader->SetInUse(true);
966     } else
967     {
968       for (f=b->brush_faces ; f ; f=f->next)
969       {
970         f->pShader->SetInUse(true);
971       }
972     }
973   }
974
975   // we are no longer showing everything
976   g_bShowAllShaders = false;
977   // put some information in the texture window title?
978   sprintf (name, "Textures: in use");
979   gtk_window_set_title (GTK_WINDOW (g_qeglobals_gui.d_entity), name);
980
981
982   // select the first texture in the list
983   if (!g_qeglobals.d_texturewin.texdef.GetName()[0])
984   {
985     SelectTexture (16, g_qeglobals.d_texturewin.height -16, false);
986   }
987 }
988
989 void Texture_ShowStartupShaders()
990 {
991   if (g_PrefsDlg.m_nShader == PrefsDlg::SHADER_COMMON)
992   {
993     // RIANT
994     // HACK FOR JK2 SUPPORT
995     if (g_pGameDescription->mGameFile == "jk2.game" || g_pGameDescription->mGameFile == "ja.game")
996     {
997       strcpy (texture_directory, "system/");
998     }
999     // RIANT
1000     // HACK FOR SOF2 SUPPORT
1001     else if (g_pGameDescription->mGameFile == "sof2.game")
1002     {
1003       strcpy (texture_directory, "tools/");
1004     }
1005     else strcpy (texture_directory, "common/");
1006     Texture_ShowDirectory ();
1007   }
1008
1009   if (g_PrefsDlg.m_nShader == PrefsDlg::SHADER_ALL) {
1010     int    count;
1011     char   filename[1024];
1012     char   *pBuff;
1013     char   dirstring[NAME_MAX];
1014     int    nLen;
1015     GSList *shaderfiles = NULL;
1016
1017     strcpy(filename, g_pGameDescription->mShaderlist.GetBuffer());
1018     count = vfsGetFileCount(filename, 0);
1019     if (count == 0)
1020     {
1021       Sys_FPrintf(SYS_ERR, "Couldn't find '%s'\n", g_pGameDescription->mShaderlist.GetBuffer());
1022       return;
1023     }
1024
1025     if (!vfsGetFullPath(filename, 0, 0))
1026     {
1027       Sys_FPrintf(SYS_ERR, "Couldn't find full path for '%s'\n", g_pGameDescription->mShaderlist.GetBuffer());
1028       Sys_FPrintf(SYS_ERR, "did you hit bug http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=130 ?\n");
1029       return;
1030     }
1031
1032     Sys_Printf("Parsing shader files from %s\n", vfsGetFullPath(filename, 0, 0));
1033     nLen = vfsLoadFile (filename, reinterpret_cast<void**>(&pBuff), 0);
1034     if (nLen > 0)
1035     {
1036       StartTokenParsing(pBuff);
1037       nLen = 0;
1038       while (GetToken(true))
1039       {
1040         GSList *tmp;
1041         bool found = false;
1042
1043         // each token should be a shader filename
1044         sprintf(dirstring, "%s.shader", token);
1045
1046         for (tmp = shaderfiles; tmp != NULL; tmp = tmp->next)
1047         {
1048           if (!strcmp (dirstring, (char*)tmp->data))
1049           {
1050             found = true;
1051             Sys_FPrintf(SYS_WRN, "duplicate entry \"%s\" in shaderlist.txt\n", (char*)tmp->data);
1052             break;
1053           }
1054         }
1055
1056         if (!found)
1057         {
1058           shaderfiles = g_slist_append (l_shaderfiles, strdup (dirstring));
1059           strcpy (texture_directory, dirstring);
1060           Texture_ShowDirectory ();
1061           nLen++;
1062         }
1063       }
1064       g_free(pBuff);
1065     }
1066   }
1067 }
1068
1069 /*
1070 ============================================================================
1071
1072 TEXTURE LAYOUT
1073
1074 TTimo: now based on a rundown through all the shaders
1075 nActiveShadersCount: number of shader that have a qtexture_t and may be displayed in the tex window
1076 nCurrentShader: index of active shader that has the current_texture
1077 pCurrentShader: IShader* for current shader
1078 NOTE: we expect the Active shaders count doesn't change during a Texture_StartPos .. Texture_NextPos cycle
1079   otherwise we may need to rely on a list instead of an array storage
1080 ============================================================================
1081 */
1082
1083 void Texture_StartPos (void)
1084 {
1085   //++timo TODO: check use of current_texture and current_row?
1086   current_x = 8;
1087   current_y = -8;
1088   current_row = 0;
1089   nActiveShadersCount = QERApp_GetActiveShaderCount();
1090   nCurrentShader = -1;
1091   current_texture = NULL;
1092   pCurrentShader = NULL;
1093 }
1094
1095 // if texture_showinuse jump over non in-use textures
1096 // it's not very clear what should be done here and what in Texture_Draw .. maybe merging the two would do good
1097 IShader* Texture_NextPos (int *x, int *y)
1098 {
1099   qtexture_t* q;
1100   while (1)
1101   {
1102     if (nCurrentShader >= nActiveShadersCount - 1)
1103     {
1104       // no more shaders
1105       current_texture = NULL;
1106       pCurrentShader = NULL;
1107       return NULL;
1108     }
1109     nCurrentShader++;
1110     pCurrentShader = QERApp_ActiveShader_ForIndex(nCurrentShader);
1111     if (pCurrentShader == NULL)
1112     {
1113       Sys_Printf("ERROR: unexpected pCurrentShader == NULL in Texture_NextPos\n");
1114       return NULL;
1115     }
1116     current_texture = pCurrentShader->getTexture();
1117     q = current_texture;
1118
1119     if (!q)
1120     {
1121       Sys_Printf("WARNING: found an IShader without qtexture_t in Texture_NextPos\n");
1122       return NULL;
1123     }
1124
1125     /*
1126     Never show anything other than "textures/" path,
1127     This is for q1/q2/q3 .map format, which expects "textures/" path on everything we apply
1128     */
1129     if(strncmp(pCurrentShader->getName(), "textures/", 9) != 0)
1130       continue;
1131
1132     // don't show shaders?
1133     if (!(g_PrefsDlg.m_bShowShaders || pCurrentShader->IsDefault()))
1134       continue;
1135
1136     if (g_PrefsDlg.m_bTextureWindow)
1137     {
1138       // some basic filtering
1139       if (!g_pParentWnd->GetTexWnd()->CheckFilter( pCurrentShader->getName() ))
1140         continue;
1141     }
1142
1143     //++timo FIXME: texture_showinuse is useless? with the menu and reload we just refresh the IsDisplayed flag
1144     // but the IsInUse is only relevant to draw the green outline
1145     if (pCurrentShader->IsDisplayed())
1146       break;
1147
1148     continue;
1149   }
1150
1151   int nWidth = (int)(q->width * ((float)g_PrefsDlg.m_nTextureScale / 100));
1152   int nHeight = (int)(q->height * ((float)g_PrefsDlg.m_nTextureScale / 100));
1153   if (current_x + nWidth > g_qeglobals.d_texturewin.width-8 && current_row)
1154   { // go to the next row unless the texture is the first on the row
1155     current_x = 8;
1156     current_y -= current_row + FONT_HEIGHT + 4;
1157     current_row = 0;
1158   }
1159
1160   *x = current_x;
1161   *y = current_y;
1162
1163   // Is our texture larger than the row? If so, grow the
1164   // row height to match it
1165
1166   if (current_row < nHeight)
1167     current_row = nHeight;
1168
1169   // never go less than 64, or the names get all crunched up
1170   current_x += nWidth < 64 ? 64 : nWidth;
1171   current_x += 8;
1172
1173   return pCurrentShader;
1174 }
1175
1176 /*
1177 ============================================================================
1178
1179   MOUSE ACTIONS
1180
1181 ============================================================================
1182 */
1183
1184 static  int textures_cursorx, textures_cursory;
1185
1186 /*
1187 ============
1188 Texture_SetTexture
1189
1190 brushprimit_texdef must be understood as a qtexture_t with width=2 height=2 ( the default one )
1191 ============
1192 */
1193
1194 //++timo NOTE: this is a mix of Shader module stuff and texture explorer
1195 // it might need to be split in parts or moved out .. dunno
1196 void WINAPI Texture_SetTexture (texdef_t *texdef, brushprimit_texdef_t *brushprimit_texdef, bool bFitScale, IPluginTexdef *pTexdef, bool bSetSelection )
1197 {
1198   if (texdef->GetName()[0] == '(')
1199   {
1200     Sys_Status("Can't select an entity texture", 0);
1201     return;
1202   }
1203   g_qeglobals.d_texturewin.texdef = *texdef;
1204   g_qeglobals.d_texturewin.texdef.flags &= ~SURF_KEEP;
1205   g_qeglobals.d_texturewin.texdef.contents &= ~CONTENTS_KEEP;
1206   // store the shader pointer
1207   // NOTE: maybe passing the shader pointer would help?
1208   g_qeglobals.d_texturewin.pShader->DecRef();
1209   g_qeglobals.d_texturewin.pShader = QERApp_Shader_ForName(texdef->GetName());
1210   g_qeglobals.d_texturewin.pShader->IncRef();
1211   // set this shader as in use
1212   g_qeglobals.d_texturewin.pShader->SetInUse( true );
1213   // store the texture coordinates for new brush primitive mode
1214   // be sure that all the callers are using the default 2x2 texture
1215   if (g_qeglobals.m_bBrushPrimitMode)
1216   {
1217     g_qeglobals.d_texturewin.brushprimit_texdef = *brushprimit_texdef;
1218   }
1219
1220   g_dlgFind.updateTextures(texdef->GetName());
1221   if (!g_dlgFind.isOpen() && bSetSelection)
1222   {
1223     Select_SetTexture(texdef,brushprimit_texdef,bFitScale);
1224   }
1225
1226   //plugins: send a message telling that the selected texture may have changed
1227   DispatchRadiantMsg( RADIANT_TEXTURE );
1228
1229   // scroll origin so the texture is completely on screen
1230   // takes texdef from g_qeglobals.d_texturewin.texdef, set above
1231   Texture_ResetPosition();
1232 }
1233
1234 void ViewShader(const char *pFile, const char *pName)
1235 {
1236   //  ask the vfs to build the full path to the file
1237   // (i.e. the first one found)
1238   char *fullName = vfsGetFullPath(pFile,0,0);
1239   if (fullName == NULL)
1240   {
1241     Sys_FPrintf (SYS_ERR, "Couldn't get a full path to the shader file: %s\n", pFile);
1242     return;
1243   }
1244
1245   char* pBuff = NULL;
1246   int nSize = vfsLoadFullPathFile(fullName, reinterpret_cast<void**>(&pBuff));
1247   if (nSize <= 0)
1248   {
1249     Sys_FPrintf (SYS_ERR, "Failed to load shader file %s\n", fullName);
1250     return;
1251   }
1252   // look for the shader declaration
1253   int nStart;
1254   CString strFind = pName;
1255   CString strLook = pBuff;
1256   strLook.MakeLower();
1257   strFind.MakeLower();
1258   // offset used when jumping over commented out definitions
1259   int nOffset = 0;
1260   while (true)
1261   {
1262     nStart = strLook.Find(strFind, nOffset);
1263     if (nStart == -1)
1264       break;
1265     // we have found something, maybe it's a commented out shader name?
1266     char *strCheck = new char[strLook.GetLength()+1];
1267     strcpy( strCheck, strLook.GetBuffer() );
1268     strCheck[nStart] = 0;
1269     char *pCheck = strrchr( strCheck, '\n' );
1270     // if there's a commentary sign in-between we'll continue
1271     if (pCheck && strstr( pCheck, "//" ))
1272     {
1273       delete[] strCheck;
1274       nOffset = nStart + 1;
1275       continue;
1276     }
1277     delete[] strCheck;
1278     nOffset = nStart;
1279     break;
1280   }
1281   // now close the file
1282   g_free(pBuff);
1283
1284   DoTextEditor (fullName, nOffset);
1285 }
1286
1287 /*
1288 ==============
1289 SelectTexture
1290
1291   By mouse click
1292 ==============
1293 */
1294 void SelectTexture (int mx, int my, bool bShift, bool bFitScale)
1295 {
1296   int   x, y;
1297   qtexture_t  *q;
1298   texdef_t  tex;
1299   brushprimit_texdef_t brushprimit_tex;
1300
1301   my += g_qeglobals.d_texturewin.originy-g_qeglobals.d_texturewin.height;
1302
1303   Texture_StartPos ();
1304   while (1)
1305   {
1306     // NOTE: return value is == pCurrentShader and pCurrentShader->getTexture == current_texture
1307     Texture_NextPos (&x, &y);
1308     q = current_texture;
1309     if (!q)
1310       break;
1311     int nWidth = (int)(q->width * ((float)g_PrefsDlg.m_nTextureScale / 100));
1312     int nHeight = (int)(q->height * ((float)g_PrefsDlg.m_nTextureScale / 100));
1313     if (mx > x && mx - x < nWidth
1314       && my < y && y - my < nHeight + FONT_HEIGHT)
1315     {
1316       if (bShift)
1317       {
1318         if (pCurrentShader->IsDefault())
1319           Sys_Printf("ERROR: %s is not a shader, it's a texture.\n", pCurrentShader->getName() );
1320         else
1321           ViewShader( pCurrentShader->getShaderFileName(), pCurrentShader->getName() );
1322       }
1323       else
1324       {
1325         memset (&tex, 0, sizeof(tex));
1326         memset (&brushprimit_tex, 0, sizeof(brushprimit_tex));
1327         if (g_qeglobals.m_bBrushPrimitMode)
1328         {
1329           // brushprimit fitted to a 2x2 texture
1330           brushprimit_tex.coords[0][0] = 1.0f;
1331           brushprimit_tex.coords[1][1] = 1.0f;
1332         }
1333         else
1334         {
1335           tex.scale[0] = g_pGameDescription->mTextureDefaultScale;
1336           tex.scale[1] = g_pGameDescription->mTextureDefaultScale;
1337         }
1338         tex.flags = pCurrentShader->getFlags();
1339         // TTimo - shader code cleanup
1340         // texdef.name is the name of the shader, not the name of the actual texture file
1341         tex.SetName(pCurrentShader->getName());
1342         // NOTE WARNING: Texture_SetTexture uses Texture_NextPos stuff to move the window position on to the texture
1343         // if there's some kind of fuckup in Texture_SetTexture you may end up with different pCurrentShader or even pCurrentShader == NULL
1344         // so we just consider pCurrentShader and current_texture are not valid after this point
1345         IShader *pAuxShader = pCurrentShader;
1346         Texture_SetTexture ( &tex, &brushprimit_tex, bFitScale, NULL); // Nurail
1347         CString strTex;
1348         CString strName;
1349         // if shader, print shader name, otherwise texture name
1350         //++timo FIXME: maybe CShader needs some properties between color / default / actual shader
1351 #ifdef _DEBUG
1352         // this one is never supposed to be set as current one
1353         if (pAuxShader->IsColor())
1354           Sys_Printf("ERROR: unexpected pCurrentShader->IsColor() in SelectTexture\n");
1355 #endif
1356         // NOTE: IsColor is false, IsDefault the only remaining property
1357         if (pAuxShader->IsDefault())
1358         {
1359           strName = q->name;
1360           // remove the "textures/" if needed
1361           if (strName.Find("textures/")!=-1)
1362             strName = strName.Mid(9);
1363         }
1364         else
1365         {
1366           strName = pAuxShader->getName();
1367         }
1368         strTex.Format("%s W: %i H: %i", strName.GetBuffer(), q->width, q->height);
1369         g_pParentWnd->SetStatusText(3, strTex);
1370       }
1371       return;
1372     }
1373   }
1374
1375   Sys_Status("Did not select a texture", 0);
1376 }
1377
1378 /*
1379 ==============
1380 Texture_MouseDown
1381 ==============
1382 */
1383 void Texture_MouseDown (int x, int y, int buttons)
1384 {
1385   Sys_GetCursorPos (&textures_cursorx, &textures_cursory);
1386
1387   // lbutton = select texture
1388   if (buttons == MK_LBUTTON || buttons == (MK_LBUTTON | MK_SHIFT) || buttons == (MK_LBUTTON | MK_CONTROL))
1389   {
1390     SelectTexture (x, g_qeglobals.d_texturewin.height - 1 - y, buttons & MK_SHIFT, buttons & MK_CONTROL);
1391     UpdateSurfaceDialog();
1392     UpdatePatchInspector();
1393   }
1394 }
1395
1396 /*
1397 ==============
1398 Texture_MouseMoved
1399 ==============
1400 */
1401
1402 void Texture_MouseMoved (int x, int y, int buttons)
1403 {
1404   int scale = 1;
1405
1406   if ( buttons & MK_SHIFT )
1407     scale = 4;
1408
1409   // rbutton = drag texture origin
1410   if (buttons & MK_RBUTTON)
1411   {
1412     Sys_GetCursorPos (&x, &y);
1413     if ( y != textures_cursory)
1414     {
1415       g_qeglobals.d_texturewin.originy += ( y-textures_cursory) * scale;
1416       if (g_qeglobals.d_texturewin.originy > 0)
1417         g_qeglobals.d_texturewin.originy = 0;
1418       Sys_SetCursorPos (textures_cursorx, textures_cursory);
1419
1420       // (g_PrefsDlg.m_bTextureScrollbar && g_qeglobals_gui.d_texture_scroll != NULL)
1421       // fixes broken texture scrolling when scrollbar is disabled
1422       GtkAdjustment *vadjustment = gtk_range_get_adjustment (GTK_RANGE (g_qeglobals_gui.d_texture_scroll));
1423       gtk_adjustment_set_value (vadjustment, abs(g_qeglobals.d_texturewin.originy));
1424       //
1425     }
1426     return;
1427   }
1428 }
1429
1430 /*
1431 ============================================================================
1432
1433 DRAWING
1434
1435 ============================================================================
1436 */
1437
1438 int imax(int iFloor, int i) { if (i>iFloor) return iFloor;return i;}
1439
1440 /*
1441 ============
1442 Texture_Draw
1443 TTimo: relying on the shaders list to display the textures
1444 we must query all qtexture_t* to manage and display through the IShaders interface
1445 this allows a plugin to completely override the texture system
1446 ============
1447 */
1448 void Texture_Draw (int width, int height)
1449 {
1450   int x, y, last_y = 0, last_height = 0, nWidth, nHeight;
1451   qtexture_t *q;
1452   char *name;
1453
1454   qglClearColor (g_qeglobals.d_savedinfo.colors[COLOR_TEXTUREBACK][0],
1455                  g_qeglobals.d_savedinfo.colors[COLOR_TEXTUREBACK][1],
1456                  g_qeglobals.d_savedinfo.colors[COLOR_TEXTUREBACK][2], 0);
1457   qglViewport (0,0,width,height);
1458   qglMatrixMode(GL_PROJECTION);
1459   qglLoadIdentity ();
1460
1461   qglClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1462   qglDisable (GL_DEPTH_TEST);
1463   qglDisable(GL_BLEND);
1464   qglOrtho (0, width, g_qeglobals.d_texturewin.originy-height, g_qeglobals.d_texturewin.originy, -100, 100);
1465   qglEnable (GL_TEXTURE_2D);
1466
1467   qglPolygonMode (GL_FRONT_AND_BACK, GL_FILL);
1468   g_qeglobals.d_texturewin.width = width;
1469   g_qeglobals.d_texturewin.height = height;
1470
1471   Texture_StartPos();
1472   for (;;)
1473   {
1474     // NOTE: return value is == pCurrentShader and pCurrentShader->getTexture == current_texture
1475     Texture_NextPos (&x, &y);
1476     q = current_texture;
1477     if (!q)
1478       break;
1479
1480     nWidth = (int)(q->width * ((float)g_PrefsDlg.m_nTextureScale / 100));
1481     nHeight = (int)(q->height * ((float)g_PrefsDlg.m_nTextureScale / 100));
1482
1483     if (y != last_y)
1484     {
1485       last_y = y;
1486       last_height = 0;
1487     }
1488     last_height = MAX (nHeight, last_height);
1489
1490     // Is this texture visible?
1491     if ((y-nHeight-FONT_HEIGHT < g_qeglobals.d_texturewin.originy)
1492         && (y > g_qeglobals.d_texturewin.originy - height))
1493     {
1494       // borders rules:
1495       // if it's the current texture, draw a thick red line, else:
1496       // shaders have a white border, simple textures don't
1497       // if !texture_showinuse: (some textures displayed may not be in use)
1498       // draw an additional square around with 0.5 1 0.5 color
1499       if (!strcmpi(g_qeglobals.d_texturewin.texdef.GetName(), pCurrentShader->getName()))
1500       {
1501               qglLineWidth (3);
1502               qglColor3f (1,0,0);
1503               qglDisable (GL_TEXTURE_2D);
1504
1505               qglBegin (GL_LINE_LOOP);
1506               qglVertex2f (x-4,y-FONT_HEIGHT+4);
1507               qglVertex2f (x-4,y-FONT_HEIGHT-nHeight-4);
1508               qglVertex2f (x+4+nWidth,y-FONT_HEIGHT-nHeight-4);
1509               qglVertex2f (x+4+nWidth,y-FONT_HEIGHT+4);
1510               qglEnd ();
1511
1512               qglEnable (GL_TEXTURE_2D);
1513               qglLineWidth (1);
1514       }
1515       else
1516       {
1517               qglLineWidth (1);
1518               // shader border:
1519               if (!pCurrentShader->IsDefault())
1520               {
1521                 qglColor3f (1,1,1);
1522                 qglDisable (GL_TEXTURE_2D);
1523
1524                 qglBegin (GL_LINE_LOOP);
1525                 qglVertex2f (x-1,y+1-FONT_HEIGHT);
1526                 qglVertex2f (x-1,y-nHeight-1-FONT_HEIGHT);
1527                 qglVertex2f (x+1+nWidth,y-nHeight-1-FONT_HEIGHT);
1528                 qglVertex2f (x+1+nWidth,y+1-FONT_HEIGHT);
1529                 qglEnd ();
1530                 qglEnable (GL_TEXTURE_2D);
1531               }
1532
1533               // highlight in-use textures
1534               if (pCurrentShader->IsInUse())
1535               {
1536                 qglColor3f (0.5,1,0.5);
1537                 qglDisable (GL_TEXTURE_2D);
1538                 qglBegin (GL_LINE_LOOP);
1539                 qglVertex2f (x-3,y+3-FONT_HEIGHT);
1540                 qglVertex2f (x-3,y-nHeight-3-FONT_HEIGHT);
1541                 qglVertex2f (x+3+nWidth,y-nHeight-3-FONT_HEIGHT);
1542                 qglVertex2f (x+3+nWidth,y+3-FONT_HEIGHT);
1543                 qglEnd ();
1544                 qglEnable (GL_TEXTURE_2D);
1545               }
1546       }
1547
1548       // Draw the texture
1549       qglBindTexture (GL_TEXTURE_2D, q->texture_number);
1550       QE_CheckOpenGLForErrors();
1551       qglColor3f (1,1,1);
1552       qglBegin (GL_QUADS);
1553       qglTexCoord2f (0,0);
1554       qglVertex2f (x,y-FONT_HEIGHT);
1555       qglTexCoord2f (1,0);
1556       qglVertex2f (x+nWidth,y-FONT_HEIGHT);
1557       qglTexCoord2f (1,1);
1558       qglVertex2f (x+nWidth,y-FONT_HEIGHT-nHeight);
1559       qglTexCoord2f (0,1);
1560       qglVertex2f (x,y-FONT_HEIGHT-nHeight);
1561       qglEnd ();
1562
1563       // draw the texture name
1564       qglDisable (GL_TEXTURE_2D);
1565       qglColor3f (1,1,1);
1566
1567       qglRasterPos2f (x, y-FONT_HEIGHT+2);
1568
1569       // don't draw the directory name
1570       name = (char*)pCurrentShader->getName();
1571       name += strlen(name);
1572       while(name != (char*)pCurrentShader->getName() && *(name-1) != '/' && *(name-1) != '\\')
1573         name--;
1574
1575       gtk_glwidget_print_string(name);
1576       qglEnable (GL_TEXTURE_2D);
1577     }
1578   }
1579
1580   g_qeglobals.d_texturewin.m_nTotalHeight = abs(y) + last_height + FONT_HEIGHT + 4;
1581
1582   // reset the current texture
1583   qglBindTexture(GL_TEXTURE_2D, 0);
1584   qglFinish();
1585 }
1586
1587 //++timo seems we only know hard inits now..
1588 //void Texture_Init (bool bHardInit)
1589 void Texture_Init()
1590 {
1591   g_qeglobals.d_qtextures = NULL;
1592   // initialize the qtexture map
1593   if (g_qeglobals.d_qtexmap)
1594   {
1595     Sys_FPrintf(SYS_ERR, "TODO: delete g_qeglobals.d_qtexmap in Texture_Init\n");
1596   }
1597   g_qeglobals.d_qtexmap = g_hash_table_new (g_str_hash, g_str_equal);
1598   // initialize .. in some cases if no default texture / project loaded it crashes
1599   memset( &g_qeglobals.d_texturewin.texdef, 0, sizeof(g_qeglobals.d_texturewin.texdef) );
1600   g_qeglobals.d_texturewin.texdef.SetName(SHADER_NOT_FOUND);
1601   g_qeglobals.d_texturewin.pShader = QERApp_Shader_ForName(SHADER_NOT_FOUND);
1602 }
1603
1604 // FIXME TTimo this needs to move to the shader module along with l_shaderlist move
1605 // preload shader files that have been listed in shaderlist.txt
1606 void PreloadShaders()
1607 {
1608   GSList *lst = l_shaderfiles;
1609   Str shadername;
1610   while (lst)
1611   {
1612     shadername = g_pGameDescription->mShaderPath;
1613     shadername += (char*)lst->data;
1614     QERApp_LoadShaderFile(shadername.GetBuffer());
1615     lst = lst->next;
1616   }
1617 }
1618
1619 // TTimo: modified to expect the reletive path to the skin as input
1620 // will look into pak files if necessary
1621 // uses the shader code to load the texture Try_Texture_ForName
1622 // modified SkinInfo accordingly to store the qtexture_t and shader name (reletive version)
1623 // the .md3 have bundled filetype extension, but they don't fit with the actual data
1624 //   ex: models/mapobjects/gargoyle.tga doesn't exist, but models/mapobjects/gargoyle.jpg can be used instead
1625 //   so we remove the extension before load attempt
1626 int WINAPI Texture_LoadSkin(char *pName, int *pnWidth, int *pnHeight)
1627 {
1628   //  byte *pic = NULL;
1629   //  byte *pic32 = NULL;
1630   int nTex = -1;
1631   qtexture_t *qtex;
1632   SkinInfo *pInfo;
1633   const char *pCleanName;
1634
1635   int nSize = g_lstSkinCache.GetSize();
1636   pCleanName = QERApp_CleanTextureName( pName, false );
1637   for (int i = 0; i < nSize; i++)
1638   {
1639     SkinInfo *pInfo = reinterpret_cast<SkinInfo*>(g_lstSkinCache.GetAt(i));
1640     if (pInfo)
1641     {
1642       if (stricmp(pCleanName, pInfo->m_strName) == 0)
1643       {
1644         return pInfo->m_nTextureBind;
1645       }
1646     }
1647   }
1648
1649   // if the load is successfull, we get back a qtexture_t
1650   // we don't need to free it, it's in g_qeglobals.d_qtextures
1651   // NOTE: we need to free the SkinInfo though..
1652   qtex = QERApp_Try_Texture_ForName( pCleanName );
1653   if (qtex)
1654   {
1655     nTex = qtex->texture_number;
1656     pInfo = new SkinInfo(qtex->name, nTex, qtex);
1657   } else
1658   {
1659     pInfo = new SkinInfo(pCleanName, -1, NULL);
1660   }
1661   g_lstSkinCache.Add(pInfo);
1662
1663   return nTex;
1664 }
1665
1666 bool TexWnd::CheckFilter( const char* name )
1667 {
1668   const char* buf = gtk_entry_get_text (GTK_ENTRY (m_pFilter));
1669   if (strstr( name, buf ) != 0)
1670     return true;
1671   return false;
1672 }
1673
1674 // =============================================================================
1675 // static functions
1676
1677 static void vertical_scroll (GtkWidget *widget, gpointer data)
1678 {
1679   ((TexWnd*)data)->OnVScroll ();
1680 }
1681
1682 static void filter_changed (GtkWidget *widget, gpointer data)
1683 {
1684   CString str;
1685   str = gtk_entry_get_text (GTK_ENTRY (widget));
1686   ((TexWnd*)data)->UpdateFilter (str);
1687 }
1688
1689 // =============================================================================
1690 // TexWnd class
1691
1692 TexWnd::TexWnd()
1693 : GLWindow (FALSE)
1694 {
1695   m_pFilter = NULL;
1696   m_bNeedRange = true;
1697 }
1698
1699 TexWnd::~TexWnd()
1700 {
1701 }
1702
1703 void TexWnd::OnCreate ()
1704 {
1705   if (!MakeCurrent ())
1706     Error ("glMakeCurrent in TexWnd::OnCreate failed");
1707
1708   g_qeglobals_gui.d_texture = m_pWidget;
1709   g_nTextureOffset = 0;
1710
1711   GtkAdjustment *vadjustment = gtk_range_get_adjustment (GTK_RANGE (g_qeglobals_gui.d_texture_scroll));
1712   gtk_signal_connect (GTK_OBJECT (vadjustment), "value_changed", GTK_SIGNAL_FUNC (vertical_scroll), this);
1713
1714   if (g_PrefsDlg.m_bTextureScrollbar)
1715     gtk_widget_show (g_qeglobals_gui.d_texture_scroll);
1716   else
1717     gtk_widget_hide (g_qeglobals_gui.d_texture_scroll);
1718   m_bNeedRange = true;
1719
1720   gtk_signal_connect (GTK_OBJECT (m_pFilter), "changed", GTK_SIGNAL_FUNC (filter_changed), this);
1721   if (g_PrefsDlg.m_bTextureWindow)
1722     gtk_widget_show (m_pFilter);
1723 }
1724
1725 void TexWnd::UpdateFilter(const char* pFilter)
1726 {
1727   g_bFilterEnabled = false;
1728   if (pFilter)
1729   {
1730     g_strFilter = pFilter;
1731     if (g_strFilter.GetLength() > 0)
1732       g_bFilterEnabled = true;
1733     QERApp_SortActiveShaders();
1734   }
1735   Sys_UpdateWindows (W_TEXTURE);
1736 }
1737
1738 void TexWnd::OnSize (int cx, int cy)
1739 {
1740   m_bNeedRange = true;
1741 }
1742
1743 void TexWnd::OnExpose ()
1744 {
1745   int nOld = g_qeglobals.d_texturewin.m_nTotalHeight;
1746   if (!MakeCurrent ())
1747   {
1748     Sys_Printf("ERROR: glXMakeCurrent failed..\n ");
1749     Sys_Printf("Please restart Radiant if the Texture view is not working\n");
1750   } else
1751   {
1752     QE_CheckOpenGLForErrors();
1753     Texture_Draw (m_pWidget->allocation.width, m_pWidget->allocation.height - g_nTextureOffset);
1754     QE_CheckOpenGLForErrors();
1755     SwapBuffers ();
1756   }
1757   if (g_PrefsDlg.m_bTextureScrollbar && (m_bNeedRange || g_qeglobals.d_texturewin.m_nTotalHeight != nOld))
1758   {
1759     GtkAdjustment *vadjustment = gtk_range_get_adjustment (GTK_RANGE (g_qeglobals_gui.d_texture_scroll));
1760
1761     vadjustment->value = -g_qeglobals.d_texturewin.originy;
1762     vadjustment->page_size = m_pWidget->allocation.height;
1763     vadjustment->page_increment = m_pWidget->allocation.height/2;
1764     vadjustment->step_increment = 20;
1765     vadjustment->lower = 0;
1766     vadjustment->upper = g_qeglobals.d_texturewin.m_nTotalHeight;
1767
1768     gtk_signal_emit_by_name (GTK_OBJECT (vadjustment), "changed");
1769
1770     m_bNeedRange = false;
1771   }
1772 }
1773
1774 void TexWnd::OnLButtonDown (guint32 flags, int pointx, int pointy)
1775 {
1776   SetCapture ();
1777   Texture_MouseDown (pointx, pointy - g_nTextureOffset, flags);
1778 }
1779
1780 void TexWnd::OnRButtonDown (guint32 flags, int pointx, int pointy)
1781 {
1782   SetCapture ();
1783   Texture_MouseDown (pointx, pointy - g_nTextureOffset, flags);
1784 }
1785
1786 void TexWnd::OnMButtonDown (guint32 flags, int pointx, int pointy)
1787 {
1788   SetCapture ();
1789   Texture_MouseDown (pointx, pointy - g_nTextureOffset, flags);
1790 }
1791
1792 void TexWnd::OnLButtonUp (guint32 flags, int pointx, int pointy)
1793 {
1794   ReleaseCapture ();
1795   // NOTE TTimo http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=23
1796   DragDropTexture (flags, pointx, pointy);
1797 }
1798
1799 void TexWnd::OnRButtonUp (guint32 flags, int pointx, int pointy)
1800 {
1801   ReleaseCapture ();
1802 }
1803
1804 void TexWnd::OnMButtonUp (guint32 flags, int pointx, int pointy)
1805 {
1806   ReleaseCapture ();
1807 }
1808
1809 void TexWnd::OnMouseMove (guint32 flags, int pointx, int pointy)
1810 {
1811   Texture_MouseMoved (pointx, pointy - g_nTextureOffset, flags);
1812   // if scrollbar is hidden, we don't seem to get an update
1813   if( !g_PrefsDlg.m_bTextureScrollbar )
1814     RedrawWindow ();
1815 }
1816
1817 void TexWnd::OnVScroll ()
1818 {
1819   GtkAdjustment *vadjustment = gtk_range_get_adjustment (GTK_RANGE (g_qeglobals_gui.d_texture_scroll));
1820
1821   g_qeglobals.d_texturewin.originy = - (int)vadjustment->value;
1822   RedrawWindow ();
1823 }
1824
1825 void TexWnd::UpdatePrefs()
1826 {
1827   if (g_PrefsDlg.m_bTextureWindow)
1828     gtk_widget_show (m_pFilter);
1829   else
1830     gtk_widget_hide (m_pFilter);
1831
1832   if (g_PrefsDlg.m_bTextureScrollbar)
1833     gtk_widget_show (g_qeglobals_gui.d_texture_scroll);
1834   else
1835     gtk_widget_hide (g_qeglobals_gui.d_texture_scroll);
1836   m_bNeedRange = true;
1837   RedrawWindow ();
1838 }
1839
1840 void TexWnd::FocusEdit()
1841 {
1842   if (GTK_WIDGET_VISIBLE (m_pFilter))
1843     gtk_window_set_focus (GTK_WINDOW (g_pParentWnd->m_pWidget), m_pFilter);
1844 }
1845
1846 void TexWnd::OnMouseWheel(bool bUp)
1847 {
1848   if (bUp)
1849   {
1850     if(g_qeglobals.d_texturewin.originy < 0) {
1851       g_qeglobals.d_texturewin.originy += g_PrefsDlg.m_nWheelInc;
1852       // clamp so we don't get jiggle if moved by less than scrollwheel increment
1853       if(g_qeglobals.d_texturewin.originy > 0) {
1854         g_qeglobals.d_texturewin.originy = 0;
1855       }
1856     }
1857   }
1858   else
1859   {
1860     if(g_qeglobals.d_texturewin.originy > (-g_qeglobals.d_texturewin.m_nTotalHeight + g_qeglobals.d_texturewin.height))
1861       g_qeglobals.d_texturewin.originy -= g_PrefsDlg.m_nWheelInc;
1862   }
1863   GtkAdjustment *vadjustment = gtk_range_get_adjustment (GTK_RANGE (g_qeglobals_gui.d_texture_scroll));
1864   gtk_adjustment_set_value (vadjustment, abs(g_qeglobals.d_texturewin.originy));
1865
1866   RedrawWindow();
1867 }
1868
1869 // NOTE TTimo
1870 // http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=23
1871 void TexWnd::DragDropTexture (guint32 flags, int pointx, int pointy)
1872 {
1873   // This gets called from leftmouse up event. We see if the mouseup is above
1874   // the camwindow. If this is the case do a trace for a surface. If we hit a
1875   // surface, texture it with the current texture.
1876
1877   int     m_ptXcheck, m_ptYcheck;
1878   int     m_ptX, m_ptY;
1879   GtkWidget *widget;
1880   gint x, y;
1881   vec3_t  dir;
1882   float   f, r, u;
1883   int     i;
1884
1885   // we only want to catch a plain mouseevent
1886   if (flags)
1887     return;
1888
1889   // see if we are above the camwindow
1890   Sys_GetCursorPos (&m_ptX, &m_ptY);
1891
1892   if (g_pParentWnd->CurrentStyle() == MainFrame::eFloating)
1893     widget = g_pParentWnd->GetCamWnd()->m_pParent;
1894   else
1895     widget = g_pParentWnd->GetCamWnd()->GetWidget();
1896
1897   get_window_pos (widget, &x, &y);
1898
1899   if (m_ptX < x || m_ptY < y ||
1900       m_ptX > x + widget->allocation.width ||
1901       m_ptY > y + widget->allocation.height)
1902     return;
1903
1904   // check if the camwindow isn't being partially hidden by another window at this point
1905   // http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=187
1906   m_ptXcheck = m_ptX;
1907   m_ptYcheck = m_ptY;
1908
1909   if( g_pParentWnd->GetCamWnd()->GetWidget()->window != gdk_window_at_pointer( &m_ptXcheck, &m_ptYcheck ) )
1910     return;
1911
1912   // calc ray direction
1913   x = m_ptX - x;
1914   y = g_pParentWnd->GetCamWnd()->Camera()->height - 1 - (m_ptY - y);
1915   u = (float)( y - ( g_pParentWnd->GetCamWnd()->Camera()->height * .5f ) ) / ( g_pParentWnd->GetCamWnd()->Camera()->height * .5f );
1916   r = (float)( x - ( g_pParentWnd->GetCamWnd()->Camera()->width * .5f ) ) / ( g_pParentWnd->GetCamWnd()->Camera()->width * .5f );
1917   f = 1;
1918
1919   for (i=0 ; i<3 ; i++)
1920     dir[i] = g_pParentWnd->GetCamWnd()->Camera()->vpn[i] * f +
1921         g_pParentWnd->GetCamWnd()->Camera()->vright[i] * r +
1922         g_pParentWnd->GetCamWnd()->Camera()->vup[i] * u;
1923   VectorNormalize (dir, dir);
1924
1925   // do a trace for a surface
1926         trace_t t;
1927
1928         t = Test_Ray (g_pParentWnd->GetCamWnd()->Camera()->origin, dir, SF_SINGLEFACE);
1929
1930   if (t.brush)
1931   {
1932     texdef_t  tex;
1933     brushprimit_texdef_t brushprimit_tex;
1934
1935     memset (&tex, 0, sizeof(tex));
1936     memset (&brushprimit_tex, 0, sizeof(brushprimit_tex));
1937     if (g_qeglobals.m_bBrushPrimitMode)
1938     {
1939       // brushprimit fitted to a 2x2 texture
1940       brushprimit_tex.coords[0][0] = 1.0f;
1941       brushprimit_tex.coords[1][1] = 1.0f;
1942     } else
1943     {
1944       tex.scale[0] = g_pGameDescription->mTextureDefaultScale;
1945       tex.scale[1] = g_pGameDescription->mTextureDefaultScale;
1946     }
1947     tex.flags = g_qeglobals.d_texturewin.texdef.flags;
1948     tex.value = g_qeglobals.d_texturewin.texdef.value;
1949     tex.contents = g_qeglobals.d_texturewin.texdef.contents;
1950     // TTimo - shader code cleanup
1951     // texdef.name is the name of the shader, not the name of the actual texture file
1952     tex.SetName(g_qeglobals.d_texturewin.texdef.GetName());
1953
1954     Undo_Start("set face textures");
1955           Undo_AddBrush(t.brush);
1956           SetFaceTexdef (t.face, &tex, &brushprimit_tex, false, NULL );
1957           Brush_Build(t.brush, false);
1958           Undo_EndBrush(t.brush);
1959           Undo_End();
1960
1961     Sys_UpdateWindows (W_CAMERA);
1962     g_pParentWnd->OnTimer ();
1963   }
1964 }