2 Copyright (c) 2001, Loki software, inc.
5 Redistribution and use in source and binary forms, with or without modification,
6 are permitted provided that the following conditions are met:
8 Redistributions of source code must retain the above copyright notice, this list
9 of conditions and the following disclaimer.
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.
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
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.
32 // Shaders Manager Plugin
34 // Leonardo Zide (leo@lokigames.com)
42 #include "missing.h" //++timo FIXME: this one is intended to go away some day, it's MFC compatibility classes
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);
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;
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)
68 static char stdName[QER_MAX_NAMELEN];
70 if (strlen(name)>QER_MAX_NAMELEN)
71 g_FuncTable.m_pfnSysFPrintf(SYS_WRN, "WARNING: name exceeds QER_MAX_NAMELEN in CleanTextureName\n");
74 strcpy (stdName, name);
75 g_FuncTable.m_pfnQE_ConvertDOSToUnixName (stdName, stdName);
76 if (stdName[strlen (name) - 4] == '.')
78 stdName[strlen (stdName) - 4] = '\0';
82 char aux[QER_MAX_NAMELEN];
83 sprintf (aux, "textures/%s", stdName);
84 strcpy (stdName, aux);
89 int WINAPI QERApp_GetActiveShaderCount ()
91 return g_ActiveShaders.GetSize ();
94 IShader *WINAPI QERApp_ActiveShader_ForIndex (int i)
96 return static_cast < CShader * >(g_ActiveShaders.GetAt (i));
99 void CShaderArray::SortShaders ()
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++)
110 pSort = static_cast < CShader * >(GetAt (i));
111 sSort = pSort->getName ();
112 jcount = aux.GetSize ();
113 for (j = 0; j < jcount; j++)
115 if (strcmp (sSort, static_cast < CShader * >(aux.GetAt (j))->getName ()) < 0)
118 aux.InsertAt (j, pSort);
120 CPtrArray::RemoveAll ();
121 CPtrArray::InsertAt (0, &aux);
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 ()
129 g_ActiveShaders.SortShaders ();
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
138 for (i = 0; i < CPtrArray::GetSize (); i++)
140 CShader *pShader = static_cast < CShader * >(CPtrArray::GetAt (i));
141 if (stricmp (pShader->getName (), name) == 0)
147 void CShader::CreateDefault (const char *name)
149 const char *stdName = QERApp_CleanTextureName (name);
150 m_strTextureName = stdName;
154 CShader *CShaderArray::Shader_ForTextureName (const char *name) const
157 // check we were given a texture name that fits the qtexture_t naming conventions
158 if (strcmp (name, QERApp_CleanTextureName (name)) != 0)
160 ("WARNING: texture name %s doesn't fit qtexture_t conventions in CShaderArray::Shader_ForTextureName\n",
164 for (i = 0; i < CPtrArray::GetSize (); i++)
166 CShader *pShader = static_cast < CShader * >(CPtrArray::GetAt (i));
167 if (strcmp (name, QERApp_CleanTextureName (pShader->getTextureName ())) == 0)
173 IShader *WINAPI QERApp_ActiveShader_ForTextureName (char *name)
175 return g_ActiveShaders.Shader_ForTextureName (name);
178 void CShaderArray::AddSingle (void *lp)
181 for (i = 0; i < CPtrArray::GetSize (); i++)
183 if (CPtrArray::GetAt (i) == lp)
187 static_cast < CShader * >(CPtrArray::GetAt (i))->IncRef();
190 void CShaderArray::operator = (const class CShaderArray & src)
195 if (CPtrArray::GetSize () != 0)
196 Sys_Printf ("WARNING: CShaderArray::operator = expects an empty array\n");
199 // now go through and IncRef
200 for (i = 0; i < CPtrArray::GetSize (); i++)
201 static_cast < IShader * >(CPtrArray::GetAt (i))->IncRef ();
204 //++timo NOTE: for debugging we may need to keep track and tell wether everything has been properly unloaded
205 void CShaderArray::ReleaseAll ()
208 int count = CPtrArray::GetSize ();
210 for (i = 0; i < count; i++)
211 static_cast < IShader * >(CPtrArray::GetAt (i))->DecRef ();
213 CPtrArray::RemoveAll ();
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
225 char name[QER_MAX_NAMELEN];
229 CPtrArray PatchShaders;
231 void PushPatch (patchMesh_t * patch)
233 patchEntry_t *pEntry = new patchEntry_s;
235 strcpy (pEntry->name, patch->pShader->getName ());
236 PatchShaders.Add (pEntry);
239 char *ShaderNameLookup (patchMesh_t * patch)
242 int count = PatchShaders.GetSize ();
243 for (i = 0; i < count; i++)
245 if (static_cast < patchEntry_t * >(PatchShaders.GetAt (i))->p == patch)
246 return static_cast < patchEntry_t * >(PatchShaders.GetAt (i))->name;
248 Sys_Printf ("ERROR: failed to lookup name in ShaderNameLookup??\n");
249 return SHADER_NOT_FOUND;
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 ()
259 brush_t *active_brushes;
260 brush_t *selected_brushes;
261 brush_t *filtered_brushes;
262 qtexture_t **d_qtextures;
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 ();
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 ();
274 for (b = active_brushes->next; b != NULL && b != active_brushes; b = b->next)
277 PushPatch (b->pPatch);
279 for (b = selected_brushes->next; b != NULL && b != selected_brushes; b = b->next)
282 PushPatch (b->pPatch);
284 for (b = filtered_brushes->next; b != NULL && b != filtered_brushes; b = b->next)
287 PushPatch (b->pPatch);
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
301 Sys_Printf ("FIXME: patch shader reload workaround (old fenris? bug 104655)\n");
304 //GtkWidget *widget = g_QglTable.m_pfn_GetQeglobalsGLWidget ();
305 GHashTable *texmap = g_ShadersTable.m_pfnQTexmap ();
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?
311 qtexture_t *pTex = *d_qtextures;
312 qtexture_t *pNextTex = pTex->next;
314 //if (widget != NULL)
315 g_QglTable.m_pfn_qglDeleteTextures (1, &pTex->texture_number);
317 g_hash_table_remove (texmap, pTex->name);
319 // all qtexture_t should be manipulated with the glib alloc handlers for now
321 *d_qtextures = pNextTex;
324 g_QglTable.m_pfn_QE_CheckOpenGLForErrors ();
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
331 void SetShader (patchMesh_t * patch)
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 ();
343 void SetShader (face_t * f)
345 // unhook current shader
346 f->pShader->DecRef();
347 // don't access the texdef! it's DEAD
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 ();
355 void Brush_RefreshShader(brush_t *b)
358 SetShader(b->pPatch);
359 else if (b->owner->eclass->fixedsize)
361 /*eclass_t *eclass = HasModel(b);
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);
370 for (face_t *f=b->brush_faces ; f ; f=f->next)
374 void WINAPI QERApp_ReloadShaders ()
377 brush_t *active_brushes;
378 brush_t *selected_brushes;
379 brush_t *filtered_brushes;
381 QERApp_FreeShaders ();
383 g_DataTable.m_pfnLstSkinCache()->RemoveAll(); //md3 skins
385 active_brushes = g_DataTable.m_pfnActiveBrushes ();
386 selected_brushes = g_DataTable.m_pfnSelectedBrushes ();
387 filtered_brushes = g_DataTable.m_pfnFilteredBrushes ();
389 // now we must reload the shader information from shaderfiles
390 g_ShadersTable.m_pfnBuildShaderList();
391 g_ShadersTable.m_pfnPreloadShaders();
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
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);
412 int WINAPI QERApp_LoadShadersFromDir (const char *path)
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++)
420 CShader *pShader = reinterpret_cast < CShader * >(g_Shaders[i]);
421 if (strstr (pShader->getShaderFileName (), path) || strstr (pShader->getName (), path))
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 ());
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
432 Sys_Printf ("WARNING: unexpected pFoo != pShader in QERApp_LoadShadersFromDir\n");
434 pFoo = NULL; // leo: shut up the compiler
441 bool CShader::Parse ()
443 char *token = g_ScripLibTable.m_pfnToken ();
445 // the parsing needs to be taken out in another module
446 // Sys_Printf("TODO: CShader::Parse\n");
448 // token is shader name (full path with a "textures\")
449 // we remove the "textures\" part
450 //setName ((char *) &token[9]));
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, "{"))
461 // we need to read until we hit a balanced }
463 while (nMatch > 0 && g_ScripLibTable.m_pfnGetToken (true))
465 if (strcmp (token, "{") == 0)
470 else if (strcmp (token, "}") == 0)
475 if (nMatch > 1) continue; // ignore layers for now
476 if (strcmpi (token, "qer_nocarve") == 0)
478 m_nFlags |= QER_NOCARVE;
480 else if (strcmpi (token, "qer_trans") == 0)
482 if (g_ScripLibTable.m_pfnGetToken (true))
484 m_fTrans = (float) atof (token);
486 m_nFlags |= QER_TRANS;
488 else if (strcmpi (token, "qer_editorimage") == 0)
490 if (g_ScripLibTable.m_pfnGetToken (true))
492 // bAddTexture changed to false to allow editorimages in other locations than "textures/"
493 m_strTextureName = QERApp_CleanTextureName (token, false);
496 else if (strcmpi (token, "qer_alphafunc") == 0)
498 if (g_ScripLibTable.m_pfnGetToken (true))
501 if(stricmp( token, "greater" ) == 0 )
503 m_nAlphaFunc = GL_GREATER;
505 else if(stricmp( token, "less" ) == 0 )
507 m_nAlphaFunc = GL_LESS;
509 else if(stricmp( token, "gequal" ) == 0 )
511 m_nAlphaFunc = GL_GEQUAL;
515 m_nFlags |= QER_ALPHAFUNC;
517 if (g_ScripLibTable.m_pfnGetToken (true))
519 m_fAlphaRef = (float) atof (token);
522 else if (strcmpi (token, "cull") == 0)
524 if (g_ScripLibTable.m_pfnGetToken (true))
526 if( stricmp( token, "none" ) == 0 || stricmp( token, "twosided" ) == 0 || stricmp( token, "disable" ) == 0 )
530 else if( stricmp( token, "back" ) == 0 || stricmp( token, "backside" ) == 0 || stricmp( token, "backsided" ) == 0 )
536 m_nFlags |= QER_CULL;
539 else if (strcmpi (token, "surfaceparm") == 0)
541 if (g_ScripLibTable.m_pfnGetToken (true))
543 if (strcmpi (token, "fog") == 0)
546 if (m_fTrans == 1.0f) // has not been explicitly set by qer_trans
551 else if (strcmpi (token, "nodraw") == 0)
553 m_nFlags |= QER_NODRAW;
555 else if (strcmpi (token, "nonsolid") == 0)
557 m_nFlags |= QER_NONSOLID;
559 else if (strcmpi (token, "water") == 0)
561 m_nFlags |= QER_WATER;
563 else if (strcmpi (token, "lava") == 0)
565 m_nFlags |= QER_LAVA;
576 void CShader::RegisterActivate ()
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;
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
597 void CShader::Try_Activate ()
599 m_pTexture = QERApp_Try_Texture_ForName (m_strTextureName.GetBuffer());
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 ()
612 m_pTexture = QERApp_Texture_ForName2 (SHADER_NOTEX);
619 void WINAPI QERApp_LoadShaderFile (const char *filename)
622 int nSize = vfsLoadFile (filename, reinterpret_cast < void **>(&pBuff), 0);
625 Sys_Printf ("Parsing shaderfile %s\n", filename);
626 g_ScripLibTable.m_pfnStartTokenParsing (pBuff);
627 while (g_ScripLibTable.m_pfnGetToken (true))
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
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 ())
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)
644 Sys_Printf ("WARNING: shader %s is already in memory, definition in %s ignored.\n",
645 pShader->getName (), filename);
653 g_Shaders.Add ((void *) pShader);
658 Sys_Printf ("Error parsing shader %s\n", pShader->getName ());
666 Sys_Printf ("Unable to read shaderfile %s\n", filename);
670 IShader *WINAPI QERApp_Try_Shader_ForName (const char *name)
672 // look for the shader
673 CShader *pShader = g_Shaders.Shader_ForName (name);
677 // we may need to load the texture or use the "shader without texture" one
678 pShader->Activate ();
679 pShader->SetDisplayed (true);
683 IShader *WINAPI QERApp_CreateShader_ForTextureName (const char *name)
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);
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 ())
698 Sys_Printf ("WARNING: Activate shader failed for %s\n",pShader->getName());
700 pShader->SetDisplayed (true);
705 IShader *WINAPI QERApp_Shader_ForName (const char *name)
707 if (name == NULL || strlen (name) == 0)
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
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);
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)
719 return QERApp_ColorShader_ForName (name);
722 CShader *pShader = static_cast < CShader * >(QERApp_Try_Shader_ForName (name));
725 pShader->SetDisplayed (true);
728 return QERApp_CreateShader_ForTextureName (name);
731 qtexture_t *WINAPI QERApp_Try_Texture_ForName (const char *name)
734 // char f1[1024], f2[1024];
735 unsigned char *pPixels = NULL;
738 // convert the texture name to the standard format we use in qtexture_t
739 const char *stdName = QERApp_CleanTextureName (name);
741 // use the hash table
742 q = (qtexture_t*)g_hash_table_lookup (g_ShadersTable.m_pfnQTexmap (), stdName);
747 for (q = g_qeglobals.d_qtextures; q; q = q->next)
749 if (!strcmp (stdName, q->name))
751 Sys_Printf ("ERROR: %s is not in texture map, but was found in texture list\n");
757 g_FuncTable.m_pfnLoadImage (name, &pPixels, &nWidth, &nHeight);
760 return NULL; // we failed
762 Sys_Printf ("LOADED: %s\n", name);
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
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);
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;
784 // push it in the map
785 g_hash_table_insert (g_ShadersTable.m_pfnQTexmap (), q->name, q);
789 int WINAPI QERApp_HasShader (const char *pName)
791 // mickey check the global shader array for existense of pName
792 CShader *pShader = g_Shaders.Shader_ForName (pName);
798 IShader *WINAPI QERApp_Shader_ForName_NoLoad (const char *pName)
800 CShader *pShader = g_Shaders.Shader_ForName (pName);
805 This should NEVER return NULL, it is the last-chance call in the load cascade
807 qtexture_t *WINAPI QERApp_Texture_ForName2 (const char *filename)
810 q = QERApp_Try_Texture_ForName (filename);
813 // not found? use "texture not found"
814 q = QERApp_Try_Texture_ForName (SHADER_NOT_FOUND);
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.");
823 void CShader::CreateColor (const char *name)
826 sscanf (name, "(%g %g %g)", m_vColor, m_vColor + 1, m_vColor + 2);
827 m_strTextureName = name;
829 // create the qtexture_t
830 qtexture_t *q1 = QERApp_Texture_ForName2 (SHADER_NOT_FOUND);
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);
839 IShader *WINAPI QERApp_ColorShader_ForName (const char *name)
841 CShader *pShader = new CShader ();
842 pShader->CreateColor (name);
843 // hook it into the shader list
845 g_Shaders.Add ((void *) pShader);
849 void CShaderArray::ReleaseForShaderFile (const char *name)
853 for (i = 0; i < CPtrArray::GetSize (); i++)
855 IShader *pShader = static_cast < IShader * >(CPtrArray::GetAt (i));
856 if (!strcmp (name, pShader->getShaderFileName ()))
859 CPtrArray::RemoveAt (i);
860 i--; // get ready for next loop
865 void WINAPI QERApp_ReloadShaderFile (const char *name)
869 brush_t *active_brushes;
870 brush_t *selected_brushes;
871 brush_t *filtered_brushes;
873 // Sys_Printf("TODO: QERApp_ReloadShaderFile\n");
875 active_brushes = g_DataTable.m_pfnActiveBrushes ();
876 selected_brushes = g_DataTable.m_pfnSelectedBrushes ();
877 filtered_brushes = g_DataTable.m_pfnFilteredBrushes ();
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");
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)
895 if (b->patchBrush && !strcmp (b->pPatch->pShader->getShaderFileName (), name))
896 SetShader (b->pPatch);
898 for (f = b->brush_faces; f; f = f->next)
899 if (!strcmp (f->pShader->getShaderFileName (), name))
902 for (b = selected_brushes->next; b != NULL && b != selected_brushes; b = b->next)
904 if (b->patchBrush && !strcmp (b->pPatch->pShader->getShaderFileName (), name))
905 SetShader (b->pPatch);
907 for (f = b->brush_faces; f; f = f->next)
908 if (!strcmp (f->pShader->getShaderFileName (), name))
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)
914 if (b->patchBrush && !strcmp (b->pPatch->pShader->getShaderFileName (), name))
915 SetShader (b->pPatch);
917 for (f = b->brush_faces; f; f = f->next)
918 if (!strcmp (f->pShader->getShaderFileName (), name))
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);
927 void CShaderArray::SetDisplayed (bool b)
930 count = CPtrArray::GetSize ();
931 for (i = 0; i < count; i++)
932 static_cast < IShader * >(CPtrArray::GetAt (i))->SetDisplayed (b);
935 void CShaderArray::SetInUse (bool b)
938 count = CPtrArray::GetSize ();
939 for (i = 0; i < count; i++)
940 static_cast < IShader * >(CPtrArray::GetAt (i))->SetInUse (b);
943 // Set the IsDisplayed flag on all active shaders
944 void WINAPI QERApp_ActiveShaders_SetDisplayed (bool b)
946 g_ActiveShaders.SetDisplayed (b);
949 void WINAPI QERApp_ActiveShaders_SetInUse (bool b)
951 g_ActiveShaders.SetInUse (b);
954 // =============================================================================
957 bool CSynapseClientShaders::RequestAPI(APIDescriptor_t *pAPI)
959 if (!strcmp(pAPI->major_name, SHADERS_MAJOR))
961 _QERShadersTable* pTable= static_cast<_QERShadersTable*>(pAPI->mpTable);
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;
987 Syn_Printf("ERROR: RequestAPI( '%s' ) not found in '%s'\n", pAPI->major_name, GetInfo());