]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - plugins/shaders/shaders.cpp
This is a major change that updates the 3rd party libs on Windows builds.
[xonotic/netradiant.git] / plugins / shaders / shaders.cpp
1 /*
2 Copyright (c) 2001, Loki software, inc.
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without modification, 
6 are permitted provided that the following conditions are met:
7
8 Redistributions of source code must retain the above copyright notice, this list 
9 of conditions and the following disclaimer.
10
11 Redistributions in binary form must reproduce the above copyright notice, this
12 list of conditions and the following disclaimer in the documentation and/or
13 other materials provided with the distribution.
14
15 Neither the name of Loki software nor the names of its contributors may be used 
16 to endorse or promote products derived from this software without specific prior 
17 written permission. 
18
19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' 
20 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
21 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
22 DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY 
23 DIRECT,INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
24 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
25 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 
26 ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
27 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
28 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
29 */
30
31 //
32 // Shaders Manager Plugin
33 //
34 // Leonardo Zide (leo@lokigames.com)
35 //
36
37 // standard headers
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include "plugin.h"
41 #include "mathlib.h"
42 #include "missing.h" //++timo FIXME: this one is intended to go away some day, it's MFC compatibility classes
43 #include "shaders.h"
44
45 // some forward declarations
46 IShader *WINAPI QERApp_Shader_ForName (const char *name);
47 qtexture_t *WINAPI QERApp_Try_Texture_ForName (const char *name);
48 qtexture_t *WINAPI QERApp_Texture_ForName2 (const char *filename);
49 IShader *WINAPI QERApp_ColorShader_ForName (const char *name);
50 void WINAPI QERApp_LoadShaderFile (const char *filename);
51
52 //++timo TODO: use stl::map !! (I tried having a look to CMap but it obviously sucks)
53 CShaderArray g_Shaders;
54 // whenever a shader gets activated / deactivated this list is updated
55 // NOTE: make sure you don't add a shader that's already in
56 // NOTE: all shaders in this array are in the main g_Shaders
57 CShaderArray g_ActiveShaders;
58
59 // clean a texture name to the qtexture_t name format we use internally
60 // NOTE: there are so many cases .. this may need to get updated to cover all of them
61 // we expect a "textures/" path on top, except if bAddTexture is set to true .. in case we add in needed
62 // NOTE: case sensitivity: the engine is case sensitive. we store the shader name with case information and save with case
63 // information as well. but we assume there won't be any case conflict and so when doing lookups based on shader name,
64 // we compare as case insensitive. That is Radiant is case insensitive, but knows that the engine is case sensitive.
65 //++timo FIXME: we need to put code somewhere to detect when two shaders that are case insensitive equal are present
66 const char *WINAPI QERApp_CleanTextureName (const char *name, bool bAddTexture = false)
67 {
68   static char stdName[QER_MAX_NAMELEN];
69 #ifdef _DEBUG
70   if (strlen(name)>QER_MAX_NAMELEN)
71     g_FuncTable.m_pfnSysFPrintf(SYS_WRN, "WARNING: name exceeds QER_MAX_NAMELEN in CleanTextureName\n");
72 #endif
73
74   strcpy (stdName, name);
75   g_FuncTable.m_pfnQE_ConvertDOSToUnixName (stdName, stdName);
76   if (stdName[strlen (name) - 4] == '.')
77     // strip extension
78     stdName[strlen (stdName) - 4] = '\0';
79
80         if (bAddTexture)
81   {
82     char aux[QER_MAX_NAMELEN];
83     sprintf (aux, "textures/%s", stdName);
84     strcpy (stdName, aux);
85         }
86   return stdName;
87 }
88
89 int WINAPI QERApp_GetActiveShaderCount ()
90 {
91   return g_ActiveShaders.GetSize ();
92 }
93
94 IShader *WINAPI QERApp_ActiveShader_ForIndex (int i)
95 {
96   return static_cast < CShader * >(g_ActiveShaders.GetAt (i));
97 }
98
99 void CShaderArray::SortShaders ()
100 {
101   CPtrArray aux;
102   int i, icount;
103   int j, jcount;
104   CShader *pSort;
105   const char *sSort;
106   // dumb sort .. would it ever grow big enough so we would have to do something clever? noooo
107   icount = CPtrArray::GetSize ();
108   for (i = 0; i < icount; i++)
109   {
110     pSort = static_cast < CShader * >(GetAt (i));
111     sSort = pSort->getName ();
112     jcount = aux.GetSize ();
113     for (j = 0; j < jcount; j++)
114     {
115       if (strcmp (sSort, static_cast < CShader * >(aux.GetAt (j))->getName ()) < 0)
116         break;
117     }
118     aux.InsertAt (j, pSort);
119   }
120   CPtrArray::RemoveAll ();
121   CPtrArray::InsertAt (0, &aux);
122 }
123
124 // will sort the active shaders list by name
125 // NOTE: it would be easier if the thing would stay sorted by using a map<name,CShader> thing
126 //++timo FIXME: would need to export that to allow external override?
127 void WINAPI QERApp_SortActiveShaders ()
128 {
129   g_ActiveShaders.SortShaders ();
130 }
131
132 // NOTE: case sensitivity
133 // although we store shader names with case information, Radiant does case insensitive searches
134 // (we assume there's no case conflict with the names)
135 CShader *CShaderArray::Shader_ForName (const char *name) const
136 {
137   int i;
138   for (i = 0; i < CPtrArray::GetSize (); i++)
139   {
140     CShader *pShader = static_cast < CShader * >(CPtrArray::GetAt (i));
141     if (stricmp (pShader->getName (), name) == 0)
142       return pShader;
143   }
144   return NULL;
145 }
146
147 void CShader::CreateDefault (const char *name)
148 {
149   const char *stdName = QERApp_CleanTextureName (name);
150   m_strTextureName = stdName;
151   setName (name);
152 }
153
154 CShader *CShaderArray::Shader_ForTextureName (const char *name) const
155 {
156 #ifdef _DEBUG
157   // check we were given a texture name that fits the qtexture_t naming conventions
158   if (strcmp (name, QERApp_CleanTextureName (name)) != 0)
159     Sys_Printf
160       ("WARNING: texture name %s doesn't fit qtexture_t conventions in CShaderArray::Shader_ForTextureName\n",
161        name);
162 #endif
163   int i;
164   for (i = 0; i < CPtrArray::GetSize (); i++)
165   {
166     CShader *pShader = static_cast < CShader * >(CPtrArray::GetAt (i));
167     if (strcmp (name, QERApp_CleanTextureName (pShader->getTextureName ())) == 0)
168       return pShader;
169   }
170   return NULL;
171 }
172
173 IShader *WINAPI QERApp_ActiveShader_ForTextureName (char *name)
174 {
175   return g_ActiveShaders.Shader_ForTextureName (name);
176 }
177
178 void CShaderArray::AddSingle (void *lp)
179 {
180   int i;
181   for (i = 0; i < CPtrArray::GetSize (); i++)
182   {
183     if (CPtrArray::GetAt (i) == lp)
184       return;
185   }
186   CPtrArray::Add (lp);
187   static_cast < CShader * >(CPtrArray::GetAt (i))->IncRef();
188 }
189
190 void CShaderArray::operator = (const class CShaderArray & src)
191 {
192   int i;
193
194 #ifdef _DEBUG
195   if (CPtrArray::GetSize () != 0)
196     Sys_Printf ("WARNING: CShaderArray::operator = expects an empty array\n");
197 #endif
198   Copy (src);
199   // now go through and IncRef
200   for (i = 0; i < CPtrArray::GetSize (); i++)
201     static_cast < IShader * >(CPtrArray::GetAt (i))->IncRef ();
202 }
203
204 //++timo NOTE: for debugging we may need to keep track and tell wether everything has been properly unloaded
205 void CShaderArray::ReleaseAll ()
206 {
207   int i;
208   int count = CPtrArray::GetSize ();
209   // decref
210   for (i = 0; i < count; i++)
211     static_cast < IShader * >(CPtrArray::GetAt (i))->DecRef ();
212   // get rid
213   CPtrArray::RemoveAll ();
214 }
215
216 // NOTE TTimo:
217 // this was hacked to work a long time ago
218 // in Loki's fenris tracker as bug #104655
219 // since that info is no longer available, and the hack has been there for so long, it's part of the code now
220 // don't remember the details, but basically across a flush and reload for the shaders
221 // we have to keep track of the patches texture names in a seperate entry
222 // not sure why anymore, but I know that doesn't happen with brushes
223 typedef struct patchEntry_s
224 {
225   char name[QER_MAX_NAMELEN];
226   patchMesh_t *p;
227 } patchEntry_t;
228
229 CPtrArray PatchShaders;
230
231 void PushPatch (patchMesh_t * patch)
232 {
233   patchEntry_t *pEntry = new patchEntry_s;
234   pEntry->p = patch;
235   strcpy (pEntry->name, patch->pShader->getName ());
236   PatchShaders.Add (pEntry);
237 }
238
239 char *ShaderNameLookup (patchMesh_t * patch)
240 {
241   int i;
242   int count = PatchShaders.GetSize ();
243   for (i = 0; i < count; i++)
244   {
245     if (static_cast < patchEntry_t * >(PatchShaders.GetAt (i))->p == patch)
246       return static_cast < patchEntry_t * >(PatchShaders.GetAt (i))->name;
247   }
248   Sys_Printf ("ERROR: failed to lookup name in ShaderNameLookup??\n");
249   return SHADER_NOT_FOUND;
250 }
251 //++timo end clean
252
253 // will free all GL binded qtextures and shaders
254 // NOTE: doesn't make much sense out of Radiant exit or called during a reload
255 void WINAPI QERApp_FreeShaders ()
256 {
257   int i;
258   brush_t *b;
259   brush_t *active_brushes;
260   brush_t *selected_brushes;
261   brush_t *filtered_brushes;
262   qtexture_t **d_qtextures;
263
264   active_brushes = g_DataTable.m_pfnActiveBrushes ();
265   selected_brushes = g_DataTable.m_pfnSelectedBrushes ();
266   filtered_brushes = g_DataTable.m_pfnFilteredBrushes ();
267   d_qtextures = g_ShadersTable.m_pfnQTextures ();
268
269   // store the shader names used by the patches
270   for (i = 0; i < PatchShaders.GetSize (); i++)
271     delete static_cast < patchMesh_t * >(PatchShaders.GetAt (i));
272   PatchShaders.RemoveAll ();
273
274   for (b = active_brushes->next; b != NULL && b != active_brushes; b = b->next)
275   {
276     if (b->patchBrush)
277       PushPatch (b->pPatch);
278   }
279   for (b = selected_brushes->next; b != NULL && b != selected_brushes; b = b->next)
280   {
281     if (b->patchBrush)
282       PushPatch (b->pPatch);
283   }
284   for (b = filtered_brushes->next; b != NULL && b != filtered_brushes; b = b->next)
285   {
286     if (b->patchBrush)
287       PushPatch (b->pPatch);
288   }
289
290   // reload shaders
291   // empty the actives shaders list
292   g_ActiveShaders.ReleaseAll ();
293   g_Shaders.ReleaseAll ();
294   // empty the main g_qeglobals.d_qtextures list
295   // FIXME: when we reload later on, we need to have the shader names
296   // for brushes it's stored in the texdef
297   // but patches don't have texdef
298   // see bug 104655 for details
299   // so the solution, build an array of patchMesh_t* and their shader names
300 #ifdef _DEBUG
301   Sys_Printf ("FIXME: patch shader reload workaround (old fenris? bug 104655)\n");
302 #endif
303
304   //GtkWidget *widget = g_QglTable.m_pfn_GetQeglobalsGLWidget ();
305   GHashTable *texmap = g_ShadersTable.m_pfnQTexmap ();
306
307   // NOTE: maybe before we'd like to set all qtexture_t in the shaders list to notex?
308   // NOTE: maybe there are some qtexture_t we don't want to erase? For plain color faces maybe?
309   while (*d_qtextures)
310   {
311     qtexture_t *pTex = *d_qtextures;
312     qtexture_t *pNextTex = pTex->next;
313
314     //if (widget != NULL)
315     g_QglTable.m_pfn_qglDeleteTextures (1, &pTex->texture_number);
316
317     g_hash_table_remove (texmap, pTex->name);
318       
319     // all qtexture_t should be manipulated with the glib alloc handlers for now
320     g_free (pTex);
321     *d_qtextures = pNextTex;
322   }
323
324   g_QglTable.m_pfn_QE_CheckOpenGLForErrors ();
325 }
326
327 // those functions are only used during a shader reload phase
328 // the patch one relies on ShaderNameLookup, a table that is being built only when a flush is performed
329 // so it's not something we want to expose publicly
330
331 void SetShader (patchMesh_t * patch)
332 {
333   // unhook current shader
334   patch->pShader->DecRef();
335   // don't access this one! it has been deleted .. it's DEAD
336   patch->d_texture = NULL;
337   // hook the new one, increment the refcount
338   // NOTE TTimo this function increments the refcount, don't incref ourselves
339   patch->pShader = QERApp_Shader_ForName (ShaderNameLookup (patch));
340   patch->d_texture = patch->pShader->getTexture ();
341 }
342
343 void SetShader (face_t * f)
344 {
345   // unhook current shader
346   f->pShader->DecRef();
347   // don't access the texdef! it's DEAD
348   f->d_texture = NULL;
349   // hook
350   // NOTE TTimo this function increments the refcount, don't incref ourselves
351   f->pShader = QERApp_Shader_ForName (f->texdef.GetName());
352   f->d_texture = f->pShader->getTexture ();
353 }
354
355 void Brush_RefreshShader(brush_t *b)
356 {
357   if (b->patchBrush)
358     SetShader(b->pPatch);
359   else if (b->owner->eclass->fixedsize)
360   {
361     /*eclass_t *eclass = HasModel(b);
362     if (eclass)
363     {
364       for(entitymodel *model = eclass->model; model!=NULL; model=model->pNext)
365         if(model && model->strSkin)
366           model->nTextureBind = g_FuncTable.m_pfnTexture_LoadSkin(((GString *)model->strSkin)->str, &model->nSkinWidth, &model->nSkinHeight);
367     }*/
368   }
369   else
370                 for (face_t *f=b->brush_faces ; f ; f=f->next)
371                         SetShader(f);
372 }
373
374 void WINAPI QERApp_ReloadShaders ()
375 {
376   brush_t *b;
377   brush_t *active_brushes;
378   brush_t *selected_brushes;
379   brush_t *filtered_brushes;
380
381   QERApp_FreeShaders ();
382
383   g_DataTable.m_pfnLstSkinCache()->RemoveAll(); //md3 skins
384
385   active_brushes = g_DataTable.m_pfnActiveBrushes ();
386   selected_brushes = g_DataTable.m_pfnSelectedBrushes ();
387   filtered_brushes = g_DataTable.m_pfnFilteredBrushes ();
388
389   // now we must reload the shader information from shaderfiles
390   g_ShadersTable.m_pfnBuildShaderList();
391   g_ShadersTable.m_pfnPreloadShaders();
392
393   // refresh the map visuals: replace our old shader objects by the new ones
394   // on brush faces we have the shader name in texdef.name
395   // on patches we have the shader name in PatchShaders
396   // while we walk through the map data, we DecRef the old shaders and push the new ones in
397   // if all goes well, most of our old shaders will get deleted on the way
398
399   // FIXME: bug 104655, when we come accross a patch, we use the above array since the d_texture is lost
400   // NOTE: both face_t and patchMesh_t store pointers to the shader and qtexture_t
401   // in an ideal world they would only store shader and access the qtexture_t through it
402   // reassign all current shaders
403   for (b = active_brushes->next; b != NULL && b != active_brushes; b = b->next)
404     Brush_RefreshShader(b);
405   for (b = selected_brushes->next; b != NULL && b != selected_brushes; b = b->next)
406     Brush_RefreshShader(b);
407   // do that to the filtered brushes as well (we might have some region compiling going on)
408   for (b = filtered_brushes->next; b != NULL && b != filtered_brushes; b = b->next)
409     Brush_RefreshShader(b);
410 }
411
412 int WINAPI QERApp_LoadShadersFromDir (const char *path)
413 {
414   int count = 0;
415   // scan g_Shaders, and call QERApp_Shader_ForName for each in the given path
416   // this will load the texture if needed and will set it in use..
417   int nSize = g_Shaders.GetSize ();
418   for (int i = 0; i < nSize; i++)
419   {
420     CShader *pShader = reinterpret_cast < CShader * >(g_Shaders[i]);
421     if (strstr (pShader->getShaderFileName (), path) || strstr (pShader->getName (), path))
422     {
423       count++;
424       // request the shader, this will load the texture if needed and set "inuse"
425       //++timo FIXME: should we put an Activate member on CShader?
426       // this QERApp_Shader_ForName call is a kind of hack
427       IShader *pFoo = QERApp_Shader_ForName (pShader->getName ());
428 #ifdef _DEBUG
429       // check we activated the right shader
430       // NOTE: if there was something else loaded, the size of g_Shaders may have changed and strange behaviours are to be expected
431       if (pFoo != pShader)
432         Sys_Printf ("WARNING: unexpected pFoo != pShader in QERApp_LoadShadersFromDir\n");
433 #else
434       pFoo = NULL;              // leo: shut up the compiler
435 #endif
436     }
437   }
438   return count;
439 }
440
441 bool CShader::Parse ()
442 {
443   char *token = g_ScripLibTable.m_pfnToken ();
444
445   // the parsing needs to be taken out in another module
446 //  Sys_Printf("TODO: CShader::Parse\n");
447
448   // token is shader name (full path with a "textures\")
449   // we remove the "textures\" part
450   //setName ((char *) &token[9]));
451         // no we don't
452         setName (token);
453   // name of the qtexture_t we'll use to represent this shader (this one has the "textures\" before)
454   const char *stdName = QERApp_CleanTextureName (token);
455   m_strTextureName = stdName; // FIXME: BC reports stdName is uninitialised?
456   g_ScripLibTable.m_pfnGetToken (true);
457   if (strcmp (token, "{"))
458     return false;
459   else
460   {
461     // we need to read until we hit a balanced }
462     int nMatch = 1;
463     while (nMatch > 0 && g_ScripLibTable.m_pfnGetToken (true))
464     {
465       if (strcmp (token, "{") == 0)
466       {
467         nMatch++;
468         continue;
469       }
470       else if (strcmp (token, "}") == 0)
471       {
472         nMatch--;
473         continue;
474       }
475       if (nMatch > 1) continue; // ignore layers for now
476       if (strcmpi (token, "qer_nocarve") == 0)
477       {
478         m_nFlags |= QER_NOCARVE;
479       }
480       else if (strcmpi (token, "qer_trans") == 0)
481       {
482               if (g_ScripLibTable.m_pfnGetToken (true))
483               {
484                 m_fTrans = (float) atof (token);
485               }
486               m_nFlags |= QER_TRANS;
487       }
488       else if (strcmpi (token, "qer_editorimage") == 0)
489       {
490               if (g_ScripLibTable.m_pfnGetToken (true))
491               {
492           // bAddTexture changed to false to allow editorimages in other locations than "textures/"
493                 m_strTextureName = QERApp_CleanTextureName (token, false);
494               }
495       }
496       else if (strcmpi (token, "qer_alphafunc") == 0)
497       {
498               if (g_ScripLibTable.m_pfnGetToken (true))
499               {
500           
501           if(stricmp( token, "greater" ) == 0 )
502           {
503                   m_nAlphaFunc = GL_GREATER;
504           }
505           else if(stricmp( token, "less" ) == 0 )
506           {
507             m_nAlphaFunc = GL_LESS;
508           }
509           else if(stricmp( token, "gequal" ) == 0 )
510           {
511             m_nAlphaFunc = GL_GEQUAL;
512           }
513
514           if( m_nAlphaFunc )
515             m_nFlags |= QER_ALPHAFUNC;
516               }
517         if (g_ScripLibTable.m_pfnGetToken (true))
518               {
519                 m_fAlphaRef = (float) atof (token);
520               }
521       }
522       else if (strcmpi (token, "cull") == 0)
523       {
524         if (g_ScripLibTable.m_pfnGetToken (true))
525               {
526           if( stricmp( token, "none" ) == 0 || stricmp( token, "twosided" ) == 0 || stricmp( token, "disable" ) == 0 )
527           {
528                   m_nCull = 2;
529           }
530           else if( stricmp( token, "back" ) == 0 || stricmp( token, "backside" ) == 0 || stricmp( token, "backsided" ) == 0 )
531           {
532                   m_nCull = 1;
533           }
534
535           if( m_nCull )
536              m_nFlags |= QER_CULL;
537         }
538       }
539       else if (strcmpi (token, "surfaceparm") == 0)
540       {
541               if (g_ScripLibTable.m_pfnGetToken (true))
542               {
543                 if (strcmpi (token, "fog") == 0)
544                 {
545             m_nFlags |= QER_FOG;
546                   if (m_fTrans == 1.0f) // has not been explicitly set by qer_trans
547                   {
548                     m_fTrans = 0.35f;
549                   }
550                 }
551           else if (strcmpi (token, "nodraw") == 0)
552           {
553             m_nFlags |= QER_NODRAW;
554           }
555           else if (strcmpi (token, "nonsolid") == 0)
556           {
557             m_nFlags |= QER_NONSOLID;
558           }
559           else if (strcmpi (token, "water") == 0)
560           {
561             m_nFlags |= QER_WATER;
562           }
563           else if (strcmpi (token, "lava") == 0)
564           {
565             m_nFlags |= QER_LAVA;
566           }
567               }
568       }
569     }
570     if (nMatch != 0)
571       return false;
572   }
573   return true;
574 }
575
576 void CShader::RegisterActivate ()
577 {
578   // fill the qtexture_t with shader information
579   //++timo FIXME: a lot of that won't be necessary, will be stored at IShader* level
580 //  strcpy (m_pTexture->shadername, m_Name);
581   // this flag is set only if we have a shaderfile name
582 //  if (m_ShaderFileName[0] != '\0')
583 //    m_pTexture->bFromShader = true;
584 //  else
585 //    m_pTexture->bFromShader = false;
586   //++timo FIXME: what do we do with that?
587   //m_pTexture->fTrans = pInfo->m_fTransValue;
588 //  m_pTexture->fTrans = 1.0f;  // if != 1.0 it's ot getting drawn in Cam_Draw
589 //  m_pTexture->nShaderFlags = m_nFlags;
590   // store in the active shaders list (if necessary)
591   g_ActiveShaders.AddSingle (this);
592   // when you activate a shader, it gets displayed in the texture browser
593   m_bDisplayed = true;
594   IncRef ();
595 }
596
597 void CShader::Try_Activate ()
598 {
599   m_pTexture = QERApp_Try_Texture_ForName (m_strTextureName.GetBuffer());
600   if (m_pTexture)
601     RegisterActivate ();
602 }
603
604 // Hydra: now returns false if the ORIGINAL shader could not be activated
605 // (missing texture, or incorrect shader script), true otherwise
606 // the shader is still activated in all cases.
607 bool CShader::Activate ()
608 {
609   Try_Activate ();
610   if (!m_pTexture)
611   {
612     m_pTexture = QERApp_Texture_ForName2 (SHADER_NOTEX);
613     RegisterActivate ();
614     return false;
615   }
616   return true;
617 }
618
619 void WINAPI QERApp_LoadShaderFile (const char *filename)
620 {
621   char *pBuff;
622   int nSize = vfsLoadFile (filename, reinterpret_cast < void **>(&pBuff), 0);
623   if (nSize > 0)
624   {
625     Sys_Printf ("Parsing shaderfile %s\n", filename);
626     g_ScripLibTable.m_pfnStartTokenParsing (pBuff);
627     while (g_ScripLibTable.m_pfnGetToken (true))
628     {
629       // first token should be the path + name.. (from base)
630       CShader *pShader = new CShader ();
631       // we want the relative filename only, it's easier for later lookup .. see QERApp_ReloadShaderFile
632       char cTmp[1024];
633       g_FuncTable.m_pfnQE_ConvertDOSToUnixName (cTmp, filename);
634       // given the vfs, we should not store the full path
635       //pShader->setShaderFileName( filename + strlen(ValueForKey(g_qeglobals.d_project_entity, "basepath")));
636       pShader->setShaderFileName (filename);
637       if (pShader->Parse ())
638       {
639               // do we already have this shader?
640               //++timo NOTE: this may a bit slow, we may need to use a map instead of a dumb list
641               if (g_Shaders.Shader_ForName (pShader->getName ()) != NULL)
642               {
643 #ifdef _DEBUG
644                 Sys_Printf ("WARNING: shader %s is already in memory, definition in %s ignored.\n",
645                             pShader->getName (), filename);
646 #endif
647                 delete pShader;
648               }
649               else
650               {
651                 pShader->IncRef ();
652
653                 g_Shaders.Add ((void *) pShader);
654               }
655       }
656       else
657       {
658               Sys_Printf ("Error parsing shader %s\n", pShader->getName ());
659               delete pShader;
660       }
661     }
662     vfsFreeFile (pBuff);
663   }
664   else
665   {
666     Sys_Printf ("Unable to read shaderfile %s\n", filename);
667   }
668 }
669
670 IShader *WINAPI QERApp_Try_Shader_ForName (const char *name)
671 {
672   // look for the shader
673   CShader *pShader = g_Shaders.Shader_ForName (name);
674   if (!pShader)
675     // not found
676     return NULL;
677   // we may need to load the texture or use the "shader without texture" one
678   pShader->Activate ();
679   pShader->SetDisplayed (true);
680   return pShader;
681 }
682
683 IShader *WINAPI QERApp_CreateShader_ForTextureName (const char *name)
684 {
685   CShader *pShader;
686   pShader = new CShader;
687   // CreateDefault expects a texture / shader name relative to the "textures" directory
688   // (cause shader names are reletive to "textures/")
689   pShader->CreateDefault (name);
690   // hook it into the shader list
691   g_Shaders.Add ((void *) pShader);
692   pShader->IncRef ();
693   // if it can't find the texture, SHADER_NOT_FOUND will be used
694   // Hydra: display an error message, so the user can quickly find a list of missing
695   // textures by looking at the console.
696   if (!pShader->Activate ())
697   {
698     Sys_Printf ("WARNING: Activate shader failed for %s\n",pShader->getName());
699   }
700   pShader->SetDisplayed (true);
701   
702   return pShader;
703 }
704
705 IShader *WINAPI QERApp_Shader_ForName (const char *name)
706 {
707   if (name == NULL || strlen (name) == 0)
708   {
709     // Hydra: This error can occur if the user loaded a map with/dropped an entity that
710     // did not set a texture name "(r g b)" - check the entity definition loader
711
712     g_FuncTable.m_pfnSysFPrintf (SYS_ERR, "FIXME: name == NULL || strlen(name) == 0 in QERApp_Shader_ForName\n");
713     return QERApp_Shader_ForName (SHADER_NOT_FOUND);
714   }
715   // entities that should be represented with plain colors instead of textures
716   // request a texture name with (r g b) (it's stored in their class_t)
717   if (name[0] == '(')
718   {
719     return QERApp_ColorShader_ForName (name);
720   }
721
722   CShader *pShader = static_cast < CShader * >(QERApp_Try_Shader_ForName (name));
723   if (pShader)
724   {
725     pShader->SetDisplayed (true);
726     return pShader;
727   }
728   return QERApp_CreateShader_ForTextureName (name);
729 }
730
731 qtexture_t *WINAPI QERApp_Try_Texture_ForName (const char *name)
732 {
733   qtexture_t *q;
734 //  char f1[1024], f2[1024];
735   unsigned char *pPixels = NULL;
736   int nWidth, nHeight;
737
738   // convert the texture name to the standard format we use in qtexture_t
739   const char *stdName = QERApp_CleanTextureName (name);
740
741   // use the hash table
742   q = (qtexture_t*)g_hash_table_lookup (g_ShadersTable.m_pfnQTexmap (), stdName);
743   if (q)
744     return q;
745
746 #ifdef QTEXMAP_DEBUG
747   for (q = g_qeglobals.d_qtextures; q; q = q->next)
748   {
749     if (!strcmp (stdName, q->name))
750     {
751       Sys_Printf ("ERROR: %s is not in texture map, but was found in texture list\n");
752       return q;
753     }
754   }
755 #endif
756
757   g_FuncTable.m_pfnLoadImage (name, &pPixels, &nWidth, &nHeight);
758   
759   if (!pPixels)
760     return NULL; // we failed
761   else
762     Sys_Printf ("LOADED: %s\n", name);
763
764   // instanciate a new qtexture_t
765   // NOTE: when called by a plugin we must make sure we have set Radiant's GL context before binding the texture
766
767   // we'll be binding the GL texture now
768   // need to check we are using a right GL context
769   // with GL plugins that have their own window, the GL context may be the plugin's, in which case loading textures will bug
770   //  g_QglTable.m_pfn_glwidget_make_current (g_QglTable.m_pfn_GetQeglobalsGLWidget ());
771   q = g_FuncTable.m_pfnLoadTextureRGBA (pPixels, nWidth, nHeight);
772   if (!q)
773     return NULL;
774   g_free (pPixels);
775
776   strcpy (q->name, name);
777   // only strip extension if extension there is!
778   if (q->name[strlen (q->name) - 4] == '.')
779     q->name[strlen (q->name) - 4] = '\0';
780   // hook into the main qtexture_t list
781   qtexture_t **d_qtextures = g_ShadersTable.m_pfnQTextures ();
782   q->next = *d_qtextures;
783   *d_qtextures = q;
784   // push it in the map
785   g_hash_table_insert (g_ShadersTable.m_pfnQTexmap (), q->name, q);
786   return q;
787 }
788
789 int WINAPI QERApp_HasShader (const char *pName)
790 {
791   //  mickey check the global shader array for existense of pName
792   CShader *pShader = g_Shaders.Shader_ForName (pName);
793   if (pShader)
794     return 1;
795   return 0;
796 }
797
798 IShader *WINAPI QERApp_Shader_ForName_NoLoad (const char *pName)
799 {
800   CShader *pShader = g_Shaders.Shader_ForName (pName);
801   return pShader;
802 }
803
804 /*!
805 This should NEVER return NULL, it is the last-chance call in the load cascade
806 */
807 qtexture_t *WINAPI QERApp_Texture_ForName2 (const char *filename)
808 {
809   qtexture_t *q;
810   q = QERApp_Try_Texture_ForName (filename);
811   if (q)
812     return q;
813   // not found? use "texture not found"
814   q = QERApp_Try_Texture_ForName (SHADER_NOT_FOUND);
815   if (q)
816     return q;
817
818   // still not found? this is a fatal error
819   g_FuncTable.m_pfnError("Failed to load " SHADER_NOT_FOUND ". Looks like your installation is broken / missing some essential elements.");
820   return NULL;
821 }
822
823 void CShader::CreateColor (const char *name)
824 {
825   // parse
826   sscanf (name, "(%g %g %g)", m_vColor, m_vColor + 1, m_vColor + 2);
827   m_strTextureName = name;
828   setName ("color");
829   // create the qtexture_t
830   qtexture_t *q1 = QERApp_Texture_ForName2 (SHADER_NOT_FOUND);
831   // copy this one
832   qtexture_t *q2 = new qtexture_t;
833   memcpy (q2, q1, sizeof (qtexture_t));
834   strcpy (q2->name, m_strTextureName.GetBuffer ());
835   VectorCopy (m_vColor, q2->color);
836   m_pTexture = q2;
837 }
838
839 IShader *WINAPI QERApp_ColorShader_ForName (const char *name)
840 {
841   CShader *pShader = new CShader ();
842   pShader->CreateColor (name);
843   // hook it into the shader list
844   pShader->IncRef ();
845   g_Shaders.Add ((void *) pShader);
846   return pShader;
847 }
848
849 void CShaderArray::ReleaseForShaderFile (const char *name)
850 {
851   int i;
852   // decref
853   for (i = 0; i < CPtrArray::GetSize (); i++)
854   {
855     IShader *pShader = static_cast < IShader * >(CPtrArray::GetAt (i));
856     if (!strcmp (name, pShader->getShaderFileName ()))
857     {
858       pShader->DecRef ();
859       CPtrArray::RemoveAt (i);
860       i--;                      // get ready for next loop
861     }
862   }
863 }
864
865 void WINAPI QERApp_ReloadShaderFile (const char *name)
866 {
867   brush_t *b;
868   face_t *f;
869   brush_t *active_brushes;
870   brush_t *selected_brushes;
871   brush_t *filtered_brushes;
872
873 //  Sys_Printf("TODO: QERApp_ReloadShaderFile\n");
874
875   active_brushes = g_DataTable.m_pfnActiveBrushes ();
876   selected_brushes = g_DataTable.m_pfnSelectedBrushes ();
877   filtered_brushes = g_DataTable.m_pfnFilteredBrushes ();
878
879 #ifdef _DEBUG
880   // check the shader name is a reletive path
881   // I hacked together a few quick tests to make sure :-)
882   if (strstr (name, ":\\") || !strstr (name, "scripts"))
883     Sys_Printf ("WARNING: is %s a reletive path to a shader file? (QERApp_ReloadShaderFile\n");
884 #endif
885
886   // in the actives and global shaders lists, decref and unhook the shaders
887   //++timo NOTE: maybe we'd like to keep track of the shaders we are unhooking?
888   g_ActiveShaders.ReleaseForShaderFile (name);
889   g_Shaders.ReleaseForShaderFile (name);
890   // go through a reload of the shader file
891   QERApp_LoadShaderFile (name);
892   // scan all the brushes, replace all the old ones by refs to their new equivalents
893   for (b = active_brushes->next; b != NULL && b != active_brushes; b = b->next)
894   {
895     if (b->patchBrush && !strcmp (b->pPatch->pShader->getShaderFileName (), name))
896       SetShader (b->pPatch);
897     else
898       for (f = b->brush_faces; f; f = f->next)
899         if (!strcmp (f->pShader->getShaderFileName (), name))
900           SetShader (f);
901   }
902   for (b = selected_brushes->next; b != NULL && b != selected_brushes; b = b->next)
903   {
904     if (b->patchBrush && !strcmp (b->pPatch->pShader->getShaderFileName (), name))
905       SetShader (b->pPatch);
906     else
907       for (f = b->brush_faces; f; f = f->next)
908         if (!strcmp (f->pShader->getShaderFileName (), name))
909           SetShader (f);
910   }
911   // do that to the filtered brushes as well (we might have some region compiling going on)
912   for (b = filtered_brushes->next; b != NULL && b != filtered_brushes; b = b->next)
913   {
914     if (b->patchBrush && !strcmp (b->pPatch->pShader->getShaderFileName (), name))
915       SetShader (b->pPatch);
916     else
917       for (f = b->brush_faces; f; f = f->next)
918         if (!strcmp (f->pShader->getShaderFileName (), name))
919           SetShader (f);
920   }
921   // call Texture_ShowInUse to clean and display only what's required
922   g_ShadersTable.m_pfnTexture_ShowInuse ();
923   QERApp_SortActiveShaders ();
924   g_FuncTable.m_pfnSysUpdateWindows (W_TEXTURE);
925 }
926
927 void CShaderArray::SetDisplayed (bool b)
928 {
929   int i, count;
930   count = CPtrArray::GetSize ();
931   for (i = 0; i < count; i++)
932     static_cast < IShader * >(CPtrArray::GetAt (i))->SetDisplayed (b);
933 }
934
935 void CShaderArray::SetInUse (bool b)
936 {
937   int i, count;
938   count = CPtrArray::GetSize ();
939   for (i = 0; i < count; i++)
940     static_cast < IShader * >(CPtrArray::GetAt (i))->SetInUse (b);
941 }
942
943 // Set the IsDisplayed flag on all active shaders
944 void WINAPI QERApp_ActiveShaders_SetDisplayed (bool b)
945 {
946   g_ActiveShaders.SetDisplayed (b);
947 }
948
949 void WINAPI QERApp_ActiveShaders_SetInUse (bool b)
950 {
951   g_ActiveShaders.SetInUse (b);
952 }
953
954 // =============================================================================
955 // SYNAPSE
956
957 bool CSynapseClientShaders::RequestAPI(APIDescriptor_t *pAPI)
958 {
959   if (!strcmp(pAPI->major_name, SHADERS_MAJOR))
960   {
961     _QERShadersTable* pTable= static_cast<_QERShadersTable*>(pAPI->mpTable);
962     
963     pTable->m_pfnFreeShaders = QERApp_FreeShaders;
964     pTable->m_pfnReloadShaders = QERApp_ReloadShaders;
965     pTable->m_pfnLoadShadersFromDir = QERApp_LoadShadersFromDir;
966     pTable->m_pfnReloadShaderFile = QERApp_ReloadShaderFile;
967     pTable->m_pfnLoadShaderFile = QERApp_LoadShaderFile;
968     pTable->m_pfnHasShader = QERApp_HasShader;
969     pTable->m_pfnTry_Shader_ForName = QERApp_Try_Shader_ForName;
970     pTable->m_pfnShader_ForName = QERApp_Shader_ForName;
971     pTable->m_pfnTry_Texture_ForName = QERApp_Try_Texture_ForName;
972     pTable->m_pfnTexture_ForName = QERApp_Texture_ForName2;
973     pTable->m_pfnGetActiveShaderCount = QERApp_GetActiveShaderCount;
974     pTable->m_pfnColorShader_ForName = QERApp_ColorShader_ForName;
975     pTable->m_pfnShader_ForName_NoLoad = QERApp_Shader_ForName_NoLoad;
976     pTable->m_pfnActiveShaders_SetInUse = QERApp_ActiveShaders_SetInUse;
977     pTable->m_pfnSortActiveShaders = QERApp_SortActiveShaders;
978     pTable->m_pfnActiveShader_ForTextureName = QERApp_ActiveShader_ForTextureName;
979     pTable->m_pfnCreateShader_ForTextureName = QERApp_CreateShader_ForTextureName;
980     pTable->m_pfnActiveShaders_SetDisplayed = QERApp_ActiveShaders_SetDisplayed;
981     pTable->m_pfnActiveShader_ForIndex = QERApp_ActiveShader_ForIndex;
982     pTable->m_pfnCleanTextureName = QERApp_CleanTextureName;    
983     
984     return true;
985   }
986
987   Syn_Printf("ERROR: RequestAPI( '%s' ) not found in '%s'\n", pAPI->major_name, GetInfo());
988   return false;
989 }