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 ){
67 static char stdName[QER_MAX_NAMELEN];
69 if ( strlen( name ) > QER_MAX_NAMELEN ) {
70 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(){
90 return g_ActiveShaders.GetSize();
93 IShader *WINAPI QERApp_ActiveShader_ForIndex( int i ){
94 return static_cast < CShader * >( g_ActiveShaders.GetAt( i ) );
97 void CShaderArray::SortShaders(){
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++ )
107 pSort = static_cast < CShader * >( GetAt( i ) );
108 sSort = pSort->getName();
109 jcount = aux.GetSize();
110 for ( j = 0; j < jcount; j++ )
112 if ( strcmp( sSort, static_cast < CShader * >( aux.GetAt( j ) )->getName() ) < 0 ) {
116 aux.InsertAt( j, pSort );
118 CPtrArray::RemoveAll();
119 CPtrArray::InsertAt( 0, &aux );
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();
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 {
134 for ( i = 0; i < CPtrArray::GetSize(); i++ )
136 CShader *pShader = static_cast < CShader * >( CPtrArray::GetAt( i ) );
137 if ( stricmp( pShader->getName(), name ) == 0 ) {
144 void CShader::CreateDefault( const char *name ){
145 const char *stdName = QERApp_CleanTextureName( name );
146 m_strTextureName = stdName;
150 CShader *CShaderArray::Shader_ForTextureName( const char *name ) const {
152 // check we were given a texture name that fits the qtexture_t naming conventions
153 if ( strcmp( name, QERApp_CleanTextureName( name ) ) != 0 ) {
155 ( "WARNING: texture name %s doesn't fit qtexture_t conventions in CShaderArray::Shader_ForTextureName\n",
160 for ( i = 0; i < CPtrArray::GetSize(); i++ )
162 CShader *pShader = static_cast < CShader * >( CPtrArray::GetAt( i ) );
163 if ( strcmp( name, QERApp_CleanTextureName( pShader->getTextureName() ) ) == 0 ) {
170 IShader *WINAPI QERApp_ActiveShader_ForTextureName( char *name ){
171 return g_ActiveShaders.Shader_ForTextureName( name );
174 void CShaderArray::AddSingle( void *lp ){
176 for ( i = 0; i < CPtrArray::GetSize(); i++ )
178 if ( CPtrArray::GetAt( i ) == lp ) {
182 CPtrArray::Add( lp );
183 static_cast < CShader * >( CPtrArray::GetAt( i ) )->IncRef();
186 void CShaderArray::operator =( const class CShaderArray & src ){
190 if ( CPtrArray::GetSize() != 0 ) {
191 Sys_Printf( "WARNING: CShaderArray::operator = expects an empty array\n" );
195 // now go through and IncRef
196 for ( i = 0; i < CPtrArray::GetSize(); i++ )
197 static_cast < IShader * >( CPtrArray::GetAt( i ) )->IncRef();
200 //++timo NOTE: for debugging we may need to keep track and tell wether everything has been properly unloaded
201 void CShaderArray::ReleaseAll(){
203 int count = CPtrArray::GetSize();
205 for ( i = 0; i < count; i++ )
206 static_cast < IShader * >( CPtrArray::GetAt( i ) )->DecRef();
208 CPtrArray::RemoveAll();
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
220 char name[QER_MAX_NAMELEN];
224 CPtrArray PatchShaders;
226 void PushPatch( patchMesh_t * patch ){
227 patchEntry_t *pEntry = new patchEntry_s;
229 strcpy( pEntry->name, patch->pShader->getName() );
230 PatchShaders.Add( pEntry );
233 char *ShaderNameLookup( patchMesh_t * patch ){
235 int count = PatchShaders.GetSize();
236 for ( i = 0; i < count; i++ )
238 if ( static_cast < patchEntry_t * >( PatchShaders.GetAt( i ) )->p == patch ) {
239 return static_cast < patchEntry_t * >( PatchShaders.GetAt( i ) )->name;
242 Sys_Printf( "ERROR: failed to lookup name in ShaderNameLookup??\n" );
243 return SHADER_NOT_FOUND;
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(){
252 brush_t *active_brushes;
253 brush_t *selected_brushes;
254 brush_t *filtered_brushes;
255 qtexture_t **d_qtextures;
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();
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();
267 for ( b = active_brushes->next; b != NULL && b != active_brushes; b = b->next )
269 if ( b->patchBrush ) {
270 PushPatch( b->pPatch );
273 for ( b = selected_brushes->next; b != NULL && b != selected_brushes; b = b->next )
275 if ( b->patchBrush ) {
276 PushPatch( b->pPatch );
279 for ( b = filtered_brushes->next; b != NULL && b != filtered_brushes; b = b->next )
281 if ( b->patchBrush ) {
282 PushPatch( b->pPatch );
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
297 Sys_Printf( "FIXME: patch shader reload workaround (old fenris? bug 104655)\n" );
300 //GtkWidget *widget = g_QglTable.m_pfn_GetQeglobalsGLWidget ();
301 GHashTable *texmap = g_ShadersTable.m_pfnQTexmap();
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 )
307 qtexture_t *pTex = *d_qtextures;
308 qtexture_t *pNextTex = pTex->next;
310 //if (widget != NULL)
311 g_QglTable.m_pfn_qglDeleteTextures( 1, &pTex->texture_number );
313 g_hash_table_remove( texmap, pTex->name );
315 // all qtexture_t should be manipulated with the glib alloc handlers for now
317 *d_qtextures = pNextTex;
320 g_QglTable.m_pfn_QE_CheckOpenGLForErrors();
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
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();
338 void SetShader( face_t * f ){
339 // unhook current shader
340 f->pShader->DecRef();
341 // don't access the texdef! it's DEAD
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();
349 void Brush_RefreshShader( brush_t *b ){
350 if ( b->patchBrush ) {
351 SetShader( b->pPatch );
353 else if ( b->owner->eclass->fixedsize ) {
354 /*eclass_t *eclass = HasModel(b);
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);
363 for ( face_t *f = b->brush_faces ; f ; f = f->next )
368 void WINAPI QERApp_ReloadShaders(){
370 brush_t *active_brushes;
371 brush_t *selected_brushes;
372 brush_t *filtered_brushes;
374 QERApp_FreeShaders();
376 g_DataTable.m_pfnLstSkinCache()->RemoveAll(); //md3 skins
378 active_brushes = g_DataTable.m_pfnActiveBrushes();
379 selected_brushes = g_DataTable.m_pfnSelectedBrushes();
380 filtered_brushes = g_DataTable.m_pfnFilteredBrushes();
382 // now we must reload the shader information from shaderfiles
383 g_ShadersTable.m_pfnBuildShaderList();
384 g_ShadersTable.m_pfnPreloadShaders();
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
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 );
405 int WINAPI QERApp_LoadShadersFromDir( const char *path ){
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++ )
412 CShader *pShader = reinterpret_cast < CShader * >( g_Shaders[i] );
413 if ( strstr( pShader->getShaderFileName(), path ) || strstr( pShader->getName(), path ) ) {
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() );
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" );
426 pFoo = NULL; // leo: shut up the compiler
433 bool CShader::Parse(){
434 char *token = g_ScripLibTable.m_pfnToken();
436 // the parsing needs to be taken out in another module
437 // Sys_Printf("TODO: CShader::Parse\n");
439 // token is shader name (full path with a "textures\")
440 // we remove the "textures\" part
441 //setName ((char *) &token[9]));
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, "{" ) ) {
453 // we need to read until we hit a balanced }
455 while ( nMatch > 0 && g_ScripLibTable.m_pfnGetToken( true ) )
457 if ( strcmp( token, "{" ) == 0 ) {
461 else if ( strcmp( token, "}" ) == 0 ) {
466 continue; // ignore layers for now
468 if ( strcmpi( token, "qer_nocarve" ) == 0 ) {
469 m_nFlags |= QER_NOCARVE;
471 else if ( strcmpi( token, "qer_trans" ) == 0 ) {
472 if ( g_ScripLibTable.m_pfnGetToken( true ) ) {
473 m_fTrans = (float) atof( token );
475 m_nFlags |= QER_TRANS;
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 );
483 else if ( strcmpi( token, "qer_alphafunc" ) == 0 ) {
484 if ( g_ScripLibTable.m_pfnGetToken( true ) ) {
486 if ( stricmp( token, "greater" ) == 0 ) {
487 m_nAlphaFunc = GL_GREATER;
489 else if ( stricmp( token, "less" ) == 0 ) {
490 m_nAlphaFunc = GL_LESS;
492 else if ( stricmp( token, "gequal" ) == 0 ) {
493 m_nAlphaFunc = GL_GEQUAL;
496 if ( m_nAlphaFunc ) {
497 m_nFlags |= QER_ALPHAFUNC;
500 if ( g_ScripLibTable.m_pfnGetToken( true ) ) {
501 m_fAlphaRef = (float) atof( token );
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 ) {
509 else if ( stricmp( token, "back" ) == 0 || stricmp( token, "backside" ) == 0 || stricmp( token, "backsided" ) == 0 ) {
514 m_nFlags |= QER_CULL;
518 else if ( strcmpi( token, "surfaceparm" ) == 0 ) {
519 if ( g_ScripLibTable.m_pfnGetToken( true ) ) {
520 if ( strcmpi( token, "fog" ) == 0 ) {
522 if ( m_fTrans == 1.0f ) { // has not been explicitly set by qer_trans
526 else if ( strcmpi( token, "nodraw" ) == 0 ) {
527 m_nFlags |= QER_NODRAW;
529 else if ( strcmpi( token, "nonsolid" ) == 0 ) {
530 m_nFlags |= QER_NONSOLID;
532 else if ( strcmpi( token, "water" ) == 0 ) {
533 m_nFlags |= QER_WATER;
535 else if ( strcmpi( token, "lava" ) == 0 ) {
536 m_nFlags |= QER_LAVA;
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;
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
568 void CShader::Try_Activate(){
569 m_pTexture = QERApp_Try_Texture_ForName( m_strTextureName.GetBuffer() );
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(){
581 m_pTexture = QERApp_Texture_ForName2( SHADER_NOTEX );
588 void WINAPI QERApp_LoadShaderFile( const char *filename ){
590 int nSize = vfsLoadFile( filename, reinterpret_cast < void **>( &pBuff ), 0 );
592 Sys_Printf( "Parsing shaderfile %s\n", filename );
593 g_ScripLibTable.m_pfnStartTokenParsing( pBuff );
594 while ( g_ScripLibTable.m_pfnGetToken( true ) )
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
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 ) {
609 Sys_Printf( "WARNING: shader %s is already in memory, definition in %s ignored.\n",
610 pShader->getName(), filename );
618 g_Shaders.Add( (void *) pShader );
623 Sys_Printf( "Error parsing shader %s\n", pShader->getName() );
627 vfsFreeFile( pBuff );
631 Sys_Printf( "Unable to read shaderfile %s\n", filename );
635 IShader *WINAPI QERApp_Try_Shader_ForName( const char *name ){
636 // look for the shader
637 CShader *pShader = g_Shaders.Shader_ForName( name );
642 // we may need to load the texture or use the "shader without texture" one
644 pShader->SetDisplayed( true );
648 IShader *WINAPI QERApp_CreateShader_ForTextureName( const char *name ){
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 );
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() );
663 pShader->SetDisplayed( true );
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
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 );
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 );
682 CShader *pShader = static_cast < CShader * >( QERApp_Try_Shader_ForName( name ) );
684 pShader->SetDisplayed( true );
687 return QERApp_CreateShader_ForTextureName( name );
690 qtexture_t *WINAPI QERApp_Try_Texture_ForName( const char *name ){
692 // char f1[1024], f2[1024];
693 unsigned char *pPixels = NULL;
696 // convert the texture name to the standard format we use in qtexture_t
697 const char *stdName = QERApp_CleanTextureName( name );
699 // use the hash table
700 q = (qtexture_t*)g_hash_table_lookup( g_ShadersTable.m_pfnQTexmap(), stdName );
706 for ( q = g_qeglobals.d_qtextures; q; q = q->next )
708 if ( !strcmp( stdName, q->name ) ) {
709 Sys_Printf( "ERROR: %s is not in texture map, but was found in texture list\n" );
715 g_FuncTable.m_pfnLoadImage( name, &pPixels, &nWidth, &nHeight );
718 return NULL; // we failed
721 Sys_Printf( "LOADED: %s\n", name );
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
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 );
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';
742 // hook into the main qtexture_t list
743 qtexture_t **d_qtextures = g_ShadersTable.m_pfnQTextures();
744 q->next = *d_qtextures;
746 // push it in the map
747 g_hash_table_insert( g_ShadersTable.m_pfnQTexmap(), q->name, q );
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 );
760 IShader *WINAPI QERApp_Shader_ForName_NoLoad( const char *pName ){
761 CShader *pShader = g_Shaders.Shader_ForName( pName );
766 This should NEVER return NULL, it is the last-chance call in the load cascade
768 qtexture_t *WINAPI QERApp_Texture_ForName2( const char *filename ){
770 q = QERApp_Try_Texture_ForName( filename );
774 // not found? use "texture not found"
775 q = QERApp_Try_Texture_ForName( SHADER_NOT_FOUND );
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." );
785 void CShader::CreateColor( const char *name ){
787 sscanf( name, "(%g %g %g)", m_vColor, m_vColor + 1, m_vColor + 2 );
788 m_strTextureName = name;
790 // create the qtexture_t
791 qtexture_t *q1 = QERApp_Texture_ForName2( SHADER_NOT_FOUND );
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 );
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
805 g_Shaders.Add( (void *) pShader );
809 void CShaderArray::ReleaseForShaderFile( const char *name ){
812 for ( i = 0; i < CPtrArray::GetSize(); i++ )
814 IShader *pShader = static_cast < IShader * >( CPtrArray::GetAt( i ) );
815 if ( !strcmp( name, pShader->getShaderFileName() ) ) {
817 CPtrArray::RemoveAt( i );
818 i--; // get ready for next loop
823 void WINAPI QERApp_ReloadShaderFile( const char *name ){
826 brush_t *active_brushes;
827 brush_t *selected_brushes;
828 brush_t *filtered_brushes;
830 // Sys_Printf("TODO: QERApp_ReloadShaderFile\n");
832 active_brushes = g_DataTable.m_pfnActiveBrushes();
833 selected_brushes = g_DataTable.m_pfnSelectedBrushes();
834 filtered_brushes = g_DataTable.m_pfnFilteredBrushes();
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" );
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 )
853 if ( b->patchBrush && !strcmp( b->pPatch->pShader->getShaderFileName(), name ) ) {
854 SetShader( b->pPatch );
857 for ( f = b->brush_faces; f; f = f->next )
858 if ( !strcmp( f->pShader->getShaderFileName(), name ) ) {
863 for ( b = selected_brushes->next; b != NULL && b != selected_brushes; b = b->next )
865 if ( b->patchBrush && !strcmp( b->pPatch->pShader->getShaderFileName(), name ) ) {
866 SetShader( b->pPatch );
869 for ( f = b->brush_faces; f; f = f->next )
870 if ( !strcmp( f->pShader->getShaderFileName(), name ) ) {
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 )
878 if ( b->patchBrush && !strcmp( b->pPatch->pShader->getShaderFileName(), name ) ) {
879 SetShader( b->pPatch );
882 for ( f = b->brush_faces; f; f = f->next )
883 if ( !strcmp( f->pShader->getShaderFileName(), name ) ) {
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 );
894 void CShaderArray::SetDisplayed( bool b ){
896 count = CPtrArray::GetSize();
897 for ( i = 0; i < count; i++ )
898 static_cast < IShader * >( CPtrArray::GetAt( i ) )->SetDisplayed( b );
901 void CShaderArray::SetInUse( bool b ){
903 count = CPtrArray::GetSize();
904 for ( i = 0; i < count; i++ )
905 static_cast < IShader * >( CPtrArray::GetAt( i ) )->SetInUse( b );
908 // Set the IsDisplayed flag on all active shaders
909 void WINAPI QERApp_ActiveShaders_SetDisplayed( bool b ){
910 g_ActiveShaders.SetDisplayed( b );
913 void WINAPI QERApp_ActiveShaders_SetInUse( bool b ){
914 g_ActiveShaders.SetInUse( b );
917 // =============================================================================
920 bool CSynapseClientShaders::RequestAPI( APIDescriptor_t *pAPI ){
921 if ( !strcmp( pAPI->major_name, SHADERS_MAJOR ) ) {
922 _QERShadersTable* pTable = static_cast<_QERShadersTable*>( pAPI->mpTable );
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;
948 Syn_Printf( "ERROR: RequestAPI( '%s' ) not found in '%s'\n", pAPI->major_name, GetInfo() );