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