mirror of
https://github.com/UberGames/GtkRadiant.git
synced 2024-12-11 04:51:10 +00:00
33efc90892
git-svn-id: svn://svn.icculus.org/gtkradiant/GtkRadiant/branches/ZeroRadiant@185 8a3a26a2-13c4-0310-b231-cf6edde360e5
989 lines
32 KiB
C++
989 lines
32 KiB
C++
/*
|
|
Copyright (c) 2001, Loki software, inc.
|
|
All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without modification,
|
|
are permitted provided that the following conditions are met:
|
|
|
|
Redistributions of source code must retain the above copyright notice, this list
|
|
of conditions and the following disclaimer.
|
|
|
|
Redistributions in binary form must reproduce the above copyright notice, this
|
|
list of conditions and the following disclaimer in the documentation and/or
|
|
other materials provided with the distribution.
|
|
|
|
Neither the name of Loki software nor the names of its contributors may be used
|
|
to endorse or promote products derived from this software without specific prior
|
|
written permission.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
|
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
|
|
DIRECT,INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
|
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
//
|
|
// Shaders Manager Plugin
|
|
//
|
|
// Leonardo Zide (leo@lokigames.com)
|
|
//
|
|
|
|
// standard headers
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include "plugin.h"
|
|
#include "mathlib.h"
|
|
#include "missing.h" //++timo FIXME: this one is intended to go away some day, it's MFC compatibility classes
|
|
#include "shaders.h"
|
|
|
|
// some forward declarations
|
|
IShader *WINAPI QERApp_Shader_ForName (const char *name);
|
|
qtexture_t *WINAPI QERApp_Try_Texture_ForName (const char *name);
|
|
qtexture_t *WINAPI QERApp_Texture_ForName2 (const char *filename);
|
|
IShader *WINAPI QERApp_ColorShader_ForName (const char *name);
|
|
void WINAPI QERApp_LoadShaderFile (const char *filename);
|
|
|
|
//++timo TODO: use stl::map !! (I tried having a look to CMap but it obviously sucks)
|
|
CShaderArray g_Shaders;
|
|
// whenever a shader gets activated / deactivated this list is updated
|
|
// NOTE: make sure you don't add a shader that's already in
|
|
// NOTE: all shaders in this array are in the main g_Shaders
|
|
CShaderArray g_ActiveShaders;
|
|
|
|
// clean a texture name to the qtexture_t name format we use internally
|
|
// NOTE: there are so many cases .. this may need to get updated to cover all of them
|
|
// we expect a "textures/" path on top, except if bAddTexture is set to true .. in case we add in needed
|
|
// NOTE: case sensitivity: the engine is case sensitive. we store the shader name with case information and save with case
|
|
// information as well. but we assume there won't be any case conflict and so when doing lookups based on shader name,
|
|
// we compare as case insensitive. That is Radiant is case insensitive, but knows that the engine is case sensitive.
|
|
//++timo FIXME: we need to put code somewhere to detect when two shaders that are case insensitive equal are present
|
|
const char *WINAPI QERApp_CleanTextureName (const char *name, bool bAddTexture = false)
|
|
{
|
|
static char stdName[QER_MAX_NAMELEN];
|
|
#ifdef _DEBUG
|
|
if (strlen(name)>QER_MAX_NAMELEN)
|
|
g_FuncTable.m_pfnSysFPrintf(SYS_WRN, "WARNING: name exceeds QER_MAX_NAMELEN in CleanTextureName\n");
|
|
#endif
|
|
|
|
strcpy (stdName, name);
|
|
g_FuncTable.m_pfnQE_ConvertDOSToUnixName (stdName, stdName);
|
|
if (stdName[strlen (name) - 4] == '.')
|
|
// strip extension
|
|
stdName[strlen (stdName) - 4] = '\0';
|
|
|
|
if (bAddTexture)
|
|
{
|
|
char aux[QER_MAX_NAMELEN];
|
|
sprintf (aux, "textures/%s", stdName);
|
|
strcpy (stdName, aux);
|
|
}
|
|
return stdName;
|
|
}
|
|
|
|
int WINAPI QERApp_GetActiveShaderCount ()
|
|
{
|
|
return g_ActiveShaders.GetSize ();
|
|
}
|
|
|
|
IShader *WINAPI QERApp_ActiveShader_ForIndex (int i)
|
|
{
|
|
return static_cast < CShader * >(g_ActiveShaders.GetAt (i));
|
|
}
|
|
|
|
void CShaderArray::SortShaders ()
|
|
{
|
|
CPtrArray aux;
|
|
int i, icount;
|
|
int j, jcount;
|
|
CShader *pSort;
|
|
const char *sSort;
|
|
// dumb sort .. would it ever grow big enough so we would have to do something clever? noooo
|
|
icount = CPtrArray::GetSize ();
|
|
for (i = 0; i < icount; i++)
|
|
{
|
|
pSort = static_cast < CShader * >(GetAt (i));
|
|
sSort = pSort->getName ();
|
|
jcount = aux.GetSize ();
|
|
for (j = 0; j < jcount; j++)
|
|
{
|
|
if (strcmp (sSort, static_cast < CShader * >(aux.GetAt (j))->getName ()) < 0)
|
|
break;
|
|
}
|
|
aux.InsertAt (j, pSort);
|
|
}
|
|
CPtrArray::RemoveAll ();
|
|
CPtrArray::InsertAt (0, &aux);
|
|
}
|
|
|
|
// will sort the active shaders list by name
|
|
// NOTE: it would be easier if the thing would stay sorted by using a map<name,CShader> thing
|
|
//++timo FIXME: would need to export that to allow external override?
|
|
void WINAPI QERApp_SortActiveShaders ()
|
|
{
|
|
g_ActiveShaders.SortShaders ();
|
|
}
|
|
|
|
// NOTE: case sensitivity
|
|
// although we store shader names with case information, Radiant does case insensitive searches
|
|
// (we assume there's no case conflict with the names)
|
|
CShader *CShaderArray::Shader_ForName (const char *name) const
|
|
{
|
|
int i;
|
|
for (i = 0; i < CPtrArray::GetSize (); i++)
|
|
{
|
|
CShader *pShader = static_cast < CShader * >(CPtrArray::GetAt (i));
|
|
if (stricmp (pShader->getName (), name) == 0)
|
|
return pShader;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void CShader::CreateDefault (const char *name)
|
|
{
|
|
const char *stdName = QERApp_CleanTextureName (name);
|
|
m_strTextureName = stdName;
|
|
setName (name);
|
|
}
|
|
|
|
CShader *CShaderArray::Shader_ForTextureName (const char *name) const
|
|
{
|
|
#ifdef _DEBUG
|
|
// check we were given a texture name that fits the qtexture_t naming conventions
|
|
if (strcmp (name, QERApp_CleanTextureName (name)) != 0)
|
|
Sys_Printf
|
|
("WARNING: texture name %s doesn't fit qtexture_t conventions in CShaderArray::Shader_ForTextureName\n",
|
|
name);
|
|
#endif
|
|
int i;
|
|
for (i = 0; i < CPtrArray::GetSize (); i++)
|
|
{
|
|
CShader *pShader = static_cast < CShader * >(CPtrArray::GetAt (i));
|
|
if (strcmp (name, QERApp_CleanTextureName (pShader->getTextureName ())) == 0)
|
|
return pShader;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
IShader *WINAPI QERApp_ActiveShader_ForTextureName (char *name)
|
|
{
|
|
return g_ActiveShaders.Shader_ForTextureName (name);
|
|
}
|
|
|
|
void CShaderArray::AddSingle (void *lp)
|
|
{
|
|
int i;
|
|
for (i = 0; i < CPtrArray::GetSize (); i++)
|
|
{
|
|
if (CPtrArray::GetAt (i) == lp)
|
|
return;
|
|
}
|
|
CPtrArray::Add (lp);
|
|
static_cast < CShader * >(CPtrArray::GetAt (i))->IncRef();
|
|
}
|
|
|
|
void CShaderArray::operator = (const class CShaderArray & src)
|
|
{
|
|
int i;
|
|
|
|
#ifdef _DEBUG
|
|
if (CPtrArray::GetSize () != 0)
|
|
Sys_Printf ("WARNING: CShaderArray::operator = expects an empty array\n");
|
|
#endif
|
|
Copy (src);
|
|
// now go through and IncRef
|
|
for (i = 0; i < CPtrArray::GetSize (); i++)
|
|
static_cast < IShader * >(CPtrArray::GetAt (i))->IncRef ();
|
|
}
|
|
|
|
//++timo NOTE: for debugging we may need to keep track and tell wether everything has been properly unloaded
|
|
void CShaderArray::ReleaseAll ()
|
|
{
|
|
int i;
|
|
int count = CPtrArray::GetSize ();
|
|
// decref
|
|
for (i = 0; i < count; i++)
|
|
static_cast < IShader * >(CPtrArray::GetAt (i))->DecRef ();
|
|
// get rid
|
|
CPtrArray::RemoveAll ();
|
|
}
|
|
|
|
// NOTE TTimo:
|
|
// this was hacked to work a long time ago
|
|
// in Loki's fenris tracker as bug #104655
|
|
// since that info is no longer available, and the hack has been there for so long, it's part of the code now
|
|
// don't remember the details, but basically across a flush and reload for the shaders
|
|
// we have to keep track of the patches texture names in a seperate entry
|
|
// not sure why anymore, but I know that doesn't happen with brushes
|
|
typedef struct patchEntry_s
|
|
{
|
|
char name[QER_MAX_NAMELEN];
|
|
patchMesh_t *p;
|
|
} patchEntry_t;
|
|
|
|
CPtrArray PatchShaders;
|
|
|
|
void PushPatch (patchMesh_t * patch)
|
|
{
|
|
patchEntry_t *pEntry = new patchEntry_s;
|
|
pEntry->p = patch;
|
|
strcpy (pEntry->name, patch->pShader->getName ());
|
|
PatchShaders.Add (pEntry);
|
|
}
|
|
|
|
char *ShaderNameLookup (patchMesh_t * patch)
|
|
{
|
|
int i;
|
|
int count = PatchShaders.GetSize ();
|
|
for (i = 0; i < count; i++)
|
|
{
|
|
if (static_cast < patchEntry_t * >(PatchShaders.GetAt (i))->p == patch)
|
|
return static_cast < patchEntry_t * >(PatchShaders.GetAt (i))->name;
|
|
}
|
|
Sys_Printf ("ERROR: failed to lookup name in ShaderNameLookup??\n");
|
|
return SHADER_NOT_FOUND;
|
|
}
|
|
//++timo end clean
|
|
|
|
// will free all GL binded qtextures and shaders
|
|
// NOTE: doesn't make much sense out of Radiant exit or called during a reload
|
|
void WINAPI QERApp_FreeShaders ()
|
|
{
|
|
int i;
|
|
brush_t *b;
|
|
brush_t *active_brushes;
|
|
brush_t *selected_brushes;
|
|
brush_t *filtered_brushes;
|
|
qtexture_t **d_qtextures;
|
|
|
|
active_brushes = g_DataTable.m_pfnActiveBrushes ();
|
|
selected_brushes = g_DataTable.m_pfnSelectedBrushes ();
|
|
filtered_brushes = g_DataTable.m_pfnFilteredBrushes ();
|
|
d_qtextures = g_ShadersTable.m_pfnQTextures ();
|
|
|
|
// store the shader names used by the patches
|
|
for (i = 0; i < PatchShaders.GetSize (); i++)
|
|
delete static_cast < patchMesh_t * >(PatchShaders.GetAt (i));
|
|
PatchShaders.RemoveAll ();
|
|
|
|
for (b = active_brushes->next; b != NULL && b != active_brushes; b = b->next)
|
|
{
|
|
if (b->patchBrush)
|
|
PushPatch (b->pPatch);
|
|
}
|
|
for (b = selected_brushes->next; b != NULL && b != selected_brushes; b = b->next)
|
|
{
|
|
if (b->patchBrush)
|
|
PushPatch (b->pPatch);
|
|
}
|
|
for (b = filtered_brushes->next; b != NULL && b != filtered_brushes; b = b->next)
|
|
{
|
|
if (b->patchBrush)
|
|
PushPatch (b->pPatch);
|
|
}
|
|
|
|
// reload shaders
|
|
// empty the actives shaders list
|
|
g_ActiveShaders.ReleaseAll ();
|
|
g_Shaders.ReleaseAll ();
|
|
// empty the main g_qeglobals.d_qtextures list
|
|
// FIXME: when we reload later on, we need to have the shader names
|
|
// for brushes it's stored in the texdef
|
|
// but patches don't have texdef
|
|
// see bug 104655 for details
|
|
// so the solution, build an array of patchMesh_t* and their shader names
|
|
#ifdef _DEBUG
|
|
Sys_Printf ("FIXME: patch shader reload workaround (old fenris? bug 104655)\n");
|
|
#endif
|
|
|
|
//GtkWidget *widget = g_QglTable.m_pfn_GetQeglobalsGLWidget ();
|
|
GHashTable *texmap = g_ShadersTable.m_pfnQTexmap ();
|
|
|
|
// NOTE: maybe before we'd like to set all qtexture_t in the shaders list to notex?
|
|
// NOTE: maybe there are some qtexture_t we don't want to erase? For plain color faces maybe?
|
|
while (*d_qtextures)
|
|
{
|
|
qtexture_t *pTex = *d_qtextures;
|
|
qtexture_t *pNextTex = pTex->next;
|
|
|
|
//if (widget != NULL)
|
|
g_QglTable.m_pfn_qglDeleteTextures (1, &pTex->texture_number);
|
|
|
|
g_hash_table_remove (texmap, pTex->name);
|
|
|
|
// all qtexture_t should be manipulated with the glib alloc handlers for now
|
|
g_free (pTex);
|
|
*d_qtextures = pNextTex;
|
|
}
|
|
|
|
g_QglTable.m_pfn_QE_CheckOpenGLForErrors ();
|
|
}
|
|
|
|
// those functions are only used during a shader reload phase
|
|
// the patch one relies on ShaderNameLookup, a table that is being built only when a flush is performed
|
|
// so it's not something we want to expose publicly
|
|
|
|
void SetShader (patchMesh_t * patch)
|
|
{
|
|
// unhook current shader
|
|
patch->pShader->DecRef();
|
|
// don't access this one! it has been deleted .. it's DEAD
|
|
patch->d_texture = NULL;
|
|
// hook the new one, increment the refcount
|
|
// NOTE TTimo this function increments the refcount, don't incref ourselves
|
|
patch->pShader = QERApp_Shader_ForName (ShaderNameLookup (patch));
|
|
patch->d_texture = patch->pShader->getTexture ();
|
|
}
|
|
|
|
void SetShader (face_t * f)
|
|
{
|
|
// unhook current shader
|
|
f->pShader->DecRef();
|
|
// don't access the texdef! it's DEAD
|
|
f->d_texture = NULL;
|
|
// hook
|
|
// NOTE TTimo this function increments the refcount, don't incref ourselves
|
|
f->pShader = QERApp_Shader_ForName (f->texdef.GetName());
|
|
f->d_texture = f->pShader->getTexture ();
|
|
}
|
|
|
|
void Brush_RefreshShader(brush_t *b)
|
|
{
|
|
if (b->patchBrush)
|
|
SetShader(b->pPatch);
|
|
else if (b->owner->eclass->fixedsize)
|
|
{
|
|
/*eclass_t *eclass = HasModel(b);
|
|
if (eclass)
|
|
{
|
|
for(entitymodel *model = eclass->model; model!=NULL; model=model->pNext)
|
|
if(model && model->strSkin)
|
|
model->nTextureBind = g_FuncTable.m_pfnTexture_LoadSkin(((GString *)model->strSkin)->str, &model->nSkinWidth, &model->nSkinHeight);
|
|
}*/
|
|
}
|
|
else
|
|
for (face_t *f=b->brush_faces ; f ; f=f->next)
|
|
SetShader(f);
|
|
}
|
|
|
|
void WINAPI QERApp_ReloadShaders ()
|
|
{
|
|
brush_t *b;
|
|
brush_t *active_brushes;
|
|
brush_t *selected_brushes;
|
|
brush_t *filtered_brushes;
|
|
|
|
QERApp_FreeShaders ();
|
|
|
|
g_DataTable.m_pfnLstSkinCache()->RemoveAll(); //md3 skins
|
|
|
|
active_brushes = g_DataTable.m_pfnActiveBrushes ();
|
|
selected_brushes = g_DataTable.m_pfnSelectedBrushes ();
|
|
filtered_brushes = g_DataTable.m_pfnFilteredBrushes ();
|
|
|
|
// now we must reload the shader information from shaderfiles
|
|
g_ShadersTable.m_pfnBuildShaderList();
|
|
g_ShadersTable.m_pfnPreloadShaders();
|
|
|
|
// refresh the map visuals: replace our old shader objects by the new ones
|
|
// on brush faces we have the shader name in texdef.name
|
|
// on patches we have the shader name in PatchShaders
|
|
// while we walk through the map data, we DecRef the old shaders and push the new ones in
|
|
// if all goes well, most of our old shaders will get deleted on the way
|
|
|
|
// FIXME: bug 104655, when we come accross a patch, we use the above array since the d_texture is lost
|
|
// NOTE: both face_t and patchMesh_t store pointers to the shader and qtexture_t
|
|
// in an ideal world they would only store shader and access the qtexture_t through it
|
|
// reassign all current shaders
|
|
for (b = active_brushes->next; b != NULL && b != active_brushes; b = b->next)
|
|
Brush_RefreshShader(b);
|
|
for (b = selected_brushes->next; b != NULL && b != selected_brushes; b = b->next)
|
|
Brush_RefreshShader(b);
|
|
// do that to the filtered brushes as well (we might have some region compiling going on)
|
|
for (b = filtered_brushes->next; b != NULL && b != filtered_brushes; b = b->next)
|
|
Brush_RefreshShader(b);
|
|
}
|
|
|
|
int WINAPI QERApp_LoadShadersFromDir (const char *path)
|
|
{
|
|
int count = 0;
|
|
// scan g_Shaders, and call QERApp_Shader_ForName for each in the given path
|
|
// this will load the texture if needed and will set it in use..
|
|
int nSize = g_Shaders.GetSize ();
|
|
for (int i = 0; i < nSize; i++)
|
|
{
|
|
CShader *pShader = reinterpret_cast < CShader * >(g_Shaders[i]);
|
|
if (strstr (pShader->getShaderFileName (), path) || strstr (pShader->getName (), path))
|
|
{
|
|
count++;
|
|
// request the shader, this will load the texture if needed and set "inuse"
|
|
//++timo FIXME: should we put an Activate member on CShader?
|
|
// this QERApp_Shader_ForName call is a kind of hack
|
|
IShader *pFoo = QERApp_Shader_ForName (pShader->getName ());
|
|
#ifdef _DEBUG
|
|
// check we activated the right shader
|
|
// NOTE: if there was something else loaded, the size of g_Shaders may have changed and strange behaviours are to be expected
|
|
if (pFoo != pShader)
|
|
Sys_Printf ("WARNING: unexpected pFoo != pShader in QERApp_LoadShadersFromDir\n");
|
|
#else
|
|
pFoo = NULL; // leo: shut up the compiler
|
|
#endif
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
|
|
bool CShader::Parse ()
|
|
{
|
|
char *token = g_ScripLibTable.m_pfnToken ();
|
|
|
|
// the parsing needs to be taken out in another module
|
|
// Sys_Printf("TODO: CShader::Parse\n");
|
|
|
|
// token is shader name (full path with a "textures\")
|
|
// we remove the "textures\" part
|
|
//setName ((char *) &token[9]));
|
|
// no we don't
|
|
setName (token);
|
|
// name of the qtexture_t we'll use to represent this shader (this one has the "textures\" before)
|
|
const char *stdName = QERApp_CleanTextureName (token);
|
|
m_strTextureName = stdName; // FIXME: BC reports stdName is uninitialised?
|
|
g_ScripLibTable.m_pfnGetToken (true);
|
|
if (strcmp (token, "{"))
|
|
return false;
|
|
else
|
|
{
|
|
// we need to read until we hit a balanced }
|
|
int nMatch = 1;
|
|
while (nMatch > 0 && g_ScripLibTable.m_pfnGetToken (true))
|
|
{
|
|
if (strcmp (token, "{") == 0)
|
|
{
|
|
nMatch++;
|
|
continue;
|
|
}
|
|
else if (strcmp (token, "}") == 0)
|
|
{
|
|
nMatch--;
|
|
continue;
|
|
}
|
|
if (nMatch > 1) continue; // ignore layers for now
|
|
if (strcmpi (token, "qer_nocarve") == 0)
|
|
{
|
|
m_nFlags |= QER_NOCARVE;
|
|
}
|
|
else if (strcmpi (token, "qer_trans") == 0)
|
|
{
|
|
if (g_ScripLibTable.m_pfnGetToken (true))
|
|
{
|
|
m_fTrans = (float) atof (token);
|
|
}
|
|
m_nFlags |= QER_TRANS;
|
|
}
|
|
else if (strcmpi (token, "qer_editorimage") == 0)
|
|
{
|
|
if (g_ScripLibTable.m_pfnGetToken (true))
|
|
{
|
|
// bAddTexture changed to false to allow editorimages in other locations than "textures/"
|
|
m_strTextureName = QERApp_CleanTextureName (token, false);
|
|
}
|
|
}
|
|
else if (strcmpi (token, "qer_alphafunc") == 0)
|
|
{
|
|
if (g_ScripLibTable.m_pfnGetToken (true))
|
|
{
|
|
|
|
if(stricmp( token, "greater" ) == 0 )
|
|
{
|
|
m_nAlphaFunc = GL_GREATER;
|
|
}
|
|
else if(stricmp( token, "less" ) == 0 )
|
|
{
|
|
m_nAlphaFunc = GL_LESS;
|
|
}
|
|
else if(stricmp( token, "gequal" ) == 0 )
|
|
{
|
|
m_nAlphaFunc = GL_GEQUAL;
|
|
}
|
|
|
|
if( m_nAlphaFunc )
|
|
m_nFlags |= QER_ALPHAFUNC;
|
|
}
|
|
if (g_ScripLibTable.m_pfnGetToken (true))
|
|
{
|
|
m_fAlphaRef = (float) atof (token);
|
|
}
|
|
}
|
|
else if (strcmpi (token, "cull") == 0)
|
|
{
|
|
if (g_ScripLibTable.m_pfnGetToken (true))
|
|
{
|
|
if( stricmp( token, "none" ) == 0 || stricmp( token, "twosided" ) == 0 || stricmp( token, "disable" ) == 0 )
|
|
{
|
|
m_nCull = 2;
|
|
}
|
|
else if( stricmp( token, "back" ) == 0 || stricmp( token, "backside" ) == 0 || stricmp( token, "backsided" ) == 0 )
|
|
{
|
|
m_nCull = 1;
|
|
}
|
|
|
|
if( m_nCull )
|
|
m_nFlags |= QER_CULL;
|
|
}
|
|
}
|
|
else if (strcmpi (token, "surfaceparm") == 0)
|
|
{
|
|
if (g_ScripLibTable.m_pfnGetToken (true))
|
|
{
|
|
if (strcmpi (token, "fog") == 0)
|
|
{
|
|
m_nFlags |= QER_FOG;
|
|
if (m_fTrans == 1.0f) // has not been explicitly set by qer_trans
|
|
{
|
|
m_fTrans = 0.35f;
|
|
}
|
|
}
|
|
else if (strcmpi (token, "nodraw") == 0)
|
|
{
|
|
m_nFlags |= QER_NODRAW;
|
|
}
|
|
else if (strcmpi (token, "nonsolid") == 0)
|
|
{
|
|
m_nFlags |= QER_NONSOLID;
|
|
}
|
|
else if (strcmpi (token, "water") == 0)
|
|
{
|
|
m_nFlags |= QER_WATER;
|
|
}
|
|
else if (strcmpi (token, "lava") == 0)
|
|
{
|
|
m_nFlags |= QER_LAVA;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (nMatch != 0)
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void CShader::RegisterActivate ()
|
|
{
|
|
// fill the qtexture_t with shader information
|
|
//++timo FIXME: a lot of that won't be necessary, will be stored at IShader* level
|
|
// strcpy (m_pTexture->shadername, m_Name);
|
|
// this flag is set only if we have a shaderfile name
|
|
// if (m_ShaderFileName[0] != '\0')
|
|
// m_pTexture->bFromShader = true;
|
|
// else
|
|
// m_pTexture->bFromShader = false;
|
|
//++timo FIXME: what do we do with that?
|
|
//m_pTexture->fTrans = pInfo->m_fTransValue;
|
|
// m_pTexture->fTrans = 1.0f; // if != 1.0 it's ot getting drawn in Cam_Draw
|
|
// m_pTexture->nShaderFlags = m_nFlags;
|
|
// store in the active shaders list (if necessary)
|
|
g_ActiveShaders.AddSingle (this);
|
|
// when you activate a shader, it gets displayed in the texture browser
|
|
m_bDisplayed = true;
|
|
IncRef ();
|
|
}
|
|
|
|
void CShader::Try_Activate ()
|
|
{
|
|
m_pTexture = QERApp_Try_Texture_ForName (m_strTextureName.GetBuffer());
|
|
if (m_pTexture)
|
|
RegisterActivate ();
|
|
}
|
|
|
|
// Hydra: now returns false if the ORIGINAL shader could not be activated
|
|
// (missing texture, or incorrect shader script), true otherwise
|
|
// the shader is still activated in all cases.
|
|
bool CShader::Activate ()
|
|
{
|
|
Try_Activate ();
|
|
if (!m_pTexture)
|
|
{
|
|
m_pTexture = QERApp_Texture_ForName2 (SHADER_NOTEX);
|
|
RegisterActivate ();
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void WINAPI QERApp_LoadShaderFile (const char *filename)
|
|
{
|
|
char *pBuff;
|
|
int nSize = vfsLoadFile (filename, reinterpret_cast < void **>(&pBuff), 0);
|
|
if (nSize > 0)
|
|
{
|
|
Sys_Printf ("Parsing shaderfile %s\n", filename);
|
|
g_ScripLibTable.m_pfnStartTokenParsing (pBuff);
|
|
while (g_ScripLibTable.m_pfnGetToken (true))
|
|
{
|
|
// first token should be the path + name.. (from base)
|
|
CShader *pShader = new CShader ();
|
|
// we want the relative filename only, it's easier for later lookup .. see QERApp_ReloadShaderFile
|
|
char cTmp[1024];
|
|
g_FuncTable.m_pfnQE_ConvertDOSToUnixName (cTmp, filename);
|
|
// given the vfs, we should not store the full path
|
|
//pShader->setShaderFileName( filename + strlen(ValueForKey(g_qeglobals.d_project_entity, "basepath")));
|
|
pShader->setShaderFileName (filename);
|
|
if (pShader->Parse ())
|
|
{
|
|
// do we already have this shader?
|
|
//++timo NOTE: this may a bit slow, we may need to use a map instead of a dumb list
|
|
if (g_Shaders.Shader_ForName (pShader->getName ()) != NULL)
|
|
{
|
|
#ifdef _DEBUG
|
|
Sys_Printf ("WARNING: shader %s is already in memory, definition in %s ignored.\n",
|
|
pShader->getName (), filename);
|
|
#endif
|
|
delete pShader;
|
|
}
|
|
else
|
|
{
|
|
pShader->IncRef ();
|
|
|
|
g_Shaders.Add ((void *) pShader);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Sys_Printf ("Error parsing shader %s\n", pShader->getName ());
|
|
delete pShader;
|
|
}
|
|
}
|
|
vfsFreeFile (pBuff);
|
|
}
|
|
else
|
|
{
|
|
Sys_Printf ("Unable to read shaderfile %s\n", filename);
|
|
}
|
|
}
|
|
|
|
IShader *WINAPI QERApp_Try_Shader_ForName (const char *name)
|
|
{
|
|
// look for the shader
|
|
CShader *pShader = g_Shaders.Shader_ForName (name);
|
|
if (!pShader)
|
|
// not found
|
|
return NULL;
|
|
// we may need to load the texture or use the "shader without texture" one
|
|
pShader->Activate ();
|
|
pShader->SetDisplayed (true);
|
|
return pShader;
|
|
}
|
|
|
|
IShader *WINAPI QERApp_CreateShader_ForTextureName (const char *name)
|
|
{
|
|
CShader *pShader;
|
|
pShader = new CShader;
|
|
// CreateDefault expects a texture / shader name relative to the "textures" directory
|
|
// (cause shader names are reletive to "textures/")
|
|
pShader->CreateDefault (name);
|
|
// hook it into the shader list
|
|
g_Shaders.Add ((void *) pShader);
|
|
pShader->IncRef ();
|
|
// if it can't find the texture, SHADER_NOT_FOUND will be used
|
|
// Hydra: display an error message, so the user can quickly find a list of missing
|
|
// textures by looking at the console.
|
|
if (!pShader->Activate ())
|
|
{
|
|
Sys_Printf ("WARNING: Activate shader failed for %s\n",pShader->getName());
|
|
}
|
|
pShader->SetDisplayed (true);
|
|
|
|
return pShader;
|
|
}
|
|
|
|
IShader *WINAPI QERApp_Shader_ForName (const char *name)
|
|
{
|
|
if (name == NULL || strlen (name) == 0)
|
|
{
|
|
// Hydra: This error can occur if the user loaded a map with/dropped an entity that
|
|
// did not set a texture name "(r g b)" - check the entity definition loader
|
|
|
|
g_FuncTable.m_pfnSysFPrintf (SYS_ERR, "FIXME: name == NULL || strlen(name) == 0 in QERApp_Shader_ForName\n");
|
|
return QERApp_Shader_ForName (SHADER_NOT_FOUND);
|
|
}
|
|
// entities that should be represented with plain colors instead of textures
|
|
// request a texture name with (r g b) (it's stored in their class_t)
|
|
if (name[0] == '(')
|
|
{
|
|
return QERApp_ColorShader_ForName (name);
|
|
}
|
|
|
|
CShader *pShader = static_cast < CShader * >(QERApp_Try_Shader_ForName (name));
|
|
if (pShader)
|
|
{
|
|
pShader->SetDisplayed (true);
|
|
return pShader;
|
|
}
|
|
return QERApp_CreateShader_ForTextureName (name);
|
|
}
|
|
|
|
qtexture_t *WINAPI QERApp_Try_Texture_ForName (const char *name)
|
|
{
|
|
qtexture_t *q;
|
|
// char f1[1024], f2[1024];
|
|
unsigned char *pPixels = NULL;
|
|
int nWidth, nHeight;
|
|
|
|
// convert the texture name to the standard format we use in qtexture_t
|
|
const char *stdName = QERApp_CleanTextureName (name);
|
|
|
|
// use the hash table
|
|
q = (qtexture_t*)g_hash_table_lookup (g_ShadersTable.m_pfnQTexmap (), stdName);
|
|
if (q)
|
|
return q;
|
|
|
|
#ifdef QTEXMAP_DEBUG
|
|
for (q = g_qeglobals.d_qtextures; q; q = q->next)
|
|
{
|
|
if (!strcmp (stdName, q->name))
|
|
{
|
|
Sys_Printf ("ERROR: %s is not in texture map, but was found in texture list\n");
|
|
return q;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
g_FuncTable.m_pfnLoadImage (name, &pPixels, &nWidth, &nHeight);
|
|
|
|
if (!pPixels)
|
|
return NULL; // we failed
|
|
else
|
|
Sys_Printf ("LOADED: %s\n", name);
|
|
|
|
// instanciate a new qtexture_t
|
|
// NOTE: when called by a plugin we must make sure we have set Radiant's GL context before binding the texture
|
|
|
|
// we'll be binding the GL texture now
|
|
// need to check we are using a right GL context
|
|
// with GL plugins that have their own window, the GL context may be the plugin's, in which case loading textures will bug
|
|
// g_QglTable.m_pfn_glwidget_make_current (g_QglTable.m_pfn_GetQeglobalsGLWidget ());
|
|
q = g_FuncTable.m_pfnLoadTextureRGBA (pPixels, nWidth, nHeight);
|
|
if (!q)
|
|
return NULL;
|
|
g_free (pPixels);
|
|
|
|
strcpy (q->name, name);
|
|
// only strip extension if extension there is!
|
|
if (q->name[strlen (q->name) - 4] == '.')
|
|
q->name[strlen (q->name) - 4] = '\0';
|
|
// hook into the main qtexture_t list
|
|
qtexture_t **d_qtextures = g_ShadersTable.m_pfnQTextures ();
|
|
q->next = *d_qtextures;
|
|
*d_qtextures = q;
|
|
// push it in the map
|
|
g_hash_table_insert (g_ShadersTable.m_pfnQTexmap (), q->name, q);
|
|
return q;
|
|
}
|
|
|
|
int WINAPI QERApp_HasShader (const char *pName)
|
|
{
|
|
// mickey check the global shader array for existense of pName
|
|
CShader *pShader = g_Shaders.Shader_ForName (pName);
|
|
if (pShader)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
IShader *WINAPI QERApp_Shader_ForName_NoLoad (const char *pName)
|
|
{
|
|
CShader *pShader = g_Shaders.Shader_ForName (pName);
|
|
return pShader;
|
|
}
|
|
|
|
/*!
|
|
This should NEVER return NULL, it is the last-chance call in the load cascade
|
|
*/
|
|
qtexture_t *WINAPI QERApp_Texture_ForName2 (const char *filename)
|
|
{
|
|
qtexture_t *q;
|
|
q = QERApp_Try_Texture_ForName (filename);
|
|
if (q)
|
|
return q;
|
|
// not found? use "texture not found"
|
|
q = QERApp_Try_Texture_ForName (SHADER_NOT_FOUND);
|
|
if (q)
|
|
return q;
|
|
|
|
// still not found? this is a fatal error
|
|
g_FuncTable.m_pfnError("Failed to load " SHADER_NOT_FOUND ". Looks like your installation is broken / missing some essential elements.");
|
|
return NULL;
|
|
}
|
|
|
|
void CShader::CreateColor (const char *name)
|
|
{
|
|
// parse
|
|
sscanf (name, "(%g %g %g)", m_vColor, m_vColor + 1, m_vColor + 2);
|
|
m_strTextureName = name;
|
|
setName ("color");
|
|
// create the qtexture_t
|
|
qtexture_t *q1 = QERApp_Texture_ForName2 (SHADER_NOT_FOUND);
|
|
// copy this one
|
|
qtexture_t *q2 = new qtexture_t;
|
|
memcpy (q2, q1, sizeof (qtexture_t));
|
|
strcpy (q2->name, m_strTextureName.GetBuffer ());
|
|
VectorCopy (m_vColor, q2->color);
|
|
m_pTexture = q2;
|
|
}
|
|
|
|
IShader *WINAPI QERApp_ColorShader_ForName (const char *name)
|
|
{
|
|
CShader *pShader = new CShader ();
|
|
pShader->CreateColor (name);
|
|
// hook it into the shader list
|
|
pShader->IncRef ();
|
|
g_Shaders.Add ((void *) pShader);
|
|
return pShader;
|
|
}
|
|
|
|
void CShaderArray::ReleaseForShaderFile (const char *name)
|
|
{
|
|
int i;
|
|
// decref
|
|
for (i = 0; i < CPtrArray::GetSize (); i++)
|
|
{
|
|
IShader *pShader = static_cast < IShader * >(CPtrArray::GetAt (i));
|
|
if (!strcmp (name, pShader->getShaderFileName ()))
|
|
{
|
|
pShader->DecRef ();
|
|
CPtrArray::RemoveAt (i);
|
|
i--; // get ready for next loop
|
|
}
|
|
}
|
|
}
|
|
|
|
void WINAPI QERApp_ReloadShaderFile (const char *name)
|
|
{
|
|
brush_t *b;
|
|
face_t *f;
|
|
brush_t *active_brushes;
|
|
brush_t *selected_brushes;
|
|
brush_t *filtered_brushes;
|
|
|
|
// Sys_Printf("TODO: QERApp_ReloadShaderFile\n");
|
|
|
|
active_brushes = g_DataTable.m_pfnActiveBrushes ();
|
|
selected_brushes = g_DataTable.m_pfnSelectedBrushes ();
|
|
filtered_brushes = g_DataTable.m_pfnFilteredBrushes ();
|
|
|
|
#ifdef _DEBUG
|
|
// check the shader name is a reletive path
|
|
// I hacked together a few quick tests to make sure :-)
|
|
if (strstr (name, ":\\") || !strstr (name, "scripts"))
|
|
Sys_Printf ("WARNING: is %s a reletive path to a shader file? (QERApp_ReloadShaderFile\n");
|
|
#endif
|
|
|
|
// in the actives and global shaders lists, decref and unhook the shaders
|
|
//++timo NOTE: maybe we'd like to keep track of the shaders we are unhooking?
|
|
g_ActiveShaders.ReleaseForShaderFile (name);
|
|
g_Shaders.ReleaseForShaderFile (name);
|
|
// go through a reload of the shader file
|
|
QERApp_LoadShaderFile (name);
|
|
// scan all the brushes, replace all the old ones by refs to their new equivalents
|
|
for (b = active_brushes->next; b != NULL && b != active_brushes; b = b->next)
|
|
{
|
|
if (b->patchBrush && !strcmp (b->pPatch->pShader->getShaderFileName (), name))
|
|
SetShader (b->pPatch);
|
|
else
|
|
for (f = b->brush_faces; f; f = f->next)
|
|
if (!strcmp (f->pShader->getShaderFileName (), name))
|
|
SetShader (f);
|
|
}
|
|
for (b = selected_brushes->next; b != NULL && b != selected_brushes; b = b->next)
|
|
{
|
|
if (b->patchBrush && !strcmp (b->pPatch->pShader->getShaderFileName (), name))
|
|
SetShader (b->pPatch);
|
|
else
|
|
for (f = b->brush_faces; f; f = f->next)
|
|
if (!strcmp (f->pShader->getShaderFileName (), name))
|
|
SetShader (f);
|
|
}
|
|
// do that to the filtered brushes as well (we might have some region compiling going on)
|
|
for (b = filtered_brushes->next; b != NULL && b != filtered_brushes; b = b->next)
|
|
{
|
|
if (b->patchBrush && !strcmp (b->pPatch->pShader->getShaderFileName (), name))
|
|
SetShader (b->pPatch);
|
|
else
|
|
for (f = b->brush_faces; f; f = f->next)
|
|
if (!strcmp (f->pShader->getShaderFileName (), name))
|
|
SetShader (f);
|
|
}
|
|
// call Texture_ShowInUse to clean and display only what's required
|
|
g_ShadersTable.m_pfnTexture_ShowInuse ();
|
|
QERApp_SortActiveShaders ();
|
|
g_FuncTable.m_pfnSysUpdateWindows (W_TEXTURE);
|
|
}
|
|
|
|
void CShaderArray::SetDisplayed (bool b)
|
|
{
|
|
int i, count;
|
|
count = CPtrArray::GetSize ();
|
|
for (i = 0; i < count; i++)
|
|
static_cast < IShader * >(CPtrArray::GetAt (i))->SetDisplayed (b);
|
|
}
|
|
|
|
void CShaderArray::SetInUse (bool b)
|
|
{
|
|
int i, count;
|
|
count = CPtrArray::GetSize ();
|
|
for (i = 0; i < count; i++)
|
|
static_cast < IShader * >(CPtrArray::GetAt (i))->SetInUse (b);
|
|
}
|
|
|
|
// Set the IsDisplayed flag on all active shaders
|
|
void WINAPI QERApp_ActiveShaders_SetDisplayed (bool b)
|
|
{
|
|
g_ActiveShaders.SetDisplayed (b);
|
|
}
|
|
|
|
void WINAPI QERApp_ActiveShaders_SetInUse (bool b)
|
|
{
|
|
g_ActiveShaders.SetInUse (b);
|
|
}
|
|
|
|
// =============================================================================
|
|
// SYNAPSE
|
|
|
|
bool CSynapseClientShaders::RequestAPI(APIDescriptor_t *pAPI)
|
|
{
|
|
if (!strcmp(pAPI->major_name, SHADERS_MAJOR))
|
|
{
|
|
_QERShadersTable* pTable= static_cast<_QERShadersTable*>(pAPI->mpTable);
|
|
|
|
pTable->m_pfnFreeShaders = QERApp_FreeShaders;
|
|
pTable->m_pfnReloadShaders = QERApp_ReloadShaders;
|
|
pTable->m_pfnLoadShadersFromDir = QERApp_LoadShadersFromDir;
|
|
pTable->m_pfnReloadShaderFile = QERApp_ReloadShaderFile;
|
|
pTable->m_pfnLoadShaderFile = QERApp_LoadShaderFile;
|
|
pTable->m_pfnHasShader = QERApp_HasShader;
|
|
pTable->m_pfnTry_Shader_ForName = QERApp_Try_Shader_ForName;
|
|
pTable->m_pfnShader_ForName = QERApp_Shader_ForName;
|
|
pTable->m_pfnTry_Texture_ForName = QERApp_Try_Texture_ForName;
|
|
pTable->m_pfnTexture_ForName = QERApp_Texture_ForName2;
|
|
pTable->m_pfnGetActiveShaderCount = QERApp_GetActiveShaderCount;
|
|
pTable->m_pfnColorShader_ForName = QERApp_ColorShader_ForName;
|
|
pTable->m_pfnShader_ForName_NoLoad = QERApp_Shader_ForName_NoLoad;
|
|
pTable->m_pfnActiveShaders_SetInUse = QERApp_ActiveShaders_SetInUse;
|
|
pTable->m_pfnSortActiveShaders = QERApp_SortActiveShaders;
|
|
pTable->m_pfnActiveShader_ForTextureName = QERApp_ActiveShader_ForTextureName;
|
|
pTable->m_pfnCreateShader_ForTextureName = QERApp_CreateShader_ForTextureName;
|
|
pTable->m_pfnActiveShaders_SetDisplayed = QERApp_ActiveShaders_SetDisplayed;
|
|
pTable->m_pfnActiveShader_ForIndex = QERApp_ActiveShader_ForIndex;
|
|
pTable->m_pfnCleanTextureName = QERApp_CleanTextureName;
|
|
|
|
return true;
|
|
}
|
|
|
|
Syn_Printf("ERROR: RequestAPI( '%s' ) not found in '%s'\n", pAPI->major_name, GetInfo());
|
|
return false;
|
|
}
|