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