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