added live shader code editing

This commit is contained in:
myT 2023-06-25 22:01:31 +02:00
parent e13385ed05
commit 2789da3a48
6 changed files with 470 additions and 166 deletions

View file

@ -286,6 +286,16 @@ static const cmdTableItem_t imgui_cmds[] =
{ "toggleguiinput", &ToggleGuiInput_f, NULL, "toggles CNQ3 GUI input capture" }
};
static const char* GetClipboardText(void*)
{
return Sys_GetClipboardData();
}
static void SetClipboardText(void*, const char* text)
{
Sys_SetClipboardData(text);
}
void CL_IMGUI_Init()
{
Cmd_RegisterArray(imgui_cmds, MODULE_CLIENT);
@ -297,6 +307,8 @@ void CL_IMGUI_Init()
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
io.ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange;
io.IniFilename = "cnq3/imgui.ini";
io.GetClipboardTextFn = &GetClipboardText;
io.SetClipboardTextFn = &SetClipboardText;
//io.MouseDrawCursor = true; // just use the operating system's
ImFontConfig fontConfig;

View file

@ -75,9 +75,15 @@ static bool drawClouds = true;
static bool forceDynamic = false;
static bool HasStaticGeo(const drawSurf_t* drawSurf)
static bool HasStaticGeo(const drawSurf_t* drawSurf, const shader_t* shader)
{
return drawSurf->staticGeoChunk > 0 && drawSurf->staticGeoChunk < ARRAY_LEN(grp.world.statChunks);
// @NOTE: the shader->isDynamic check is needed because of the shader editing feature
// without it, an edited shader that changes vertex attributes won't display correctly
// because the original "baked" vertex attributes would be used instead
return
!shader->isDynamic &&
drawSurf->staticGeoChunk > 0 &&
drawSurf->staticGeoChunk < ARRAY_LEN(grp.world.statChunks);
}
static void UpdateEntityData(bool& depthHack, int entityNum, double originalTime)
@ -919,7 +925,7 @@ void World::DrawSceneView(const drawSceneViewCommand_t& cmd)
}
}
const bool hasStaticGeo = forceDynamic ? false : HasStaticGeo(drawSurf);
const bool hasStaticGeo = forceDynamic ? false : HasStaticGeo(drawSurf, shader);
const bool staticChanged = hasStaticGeo != oldHasStaticGeo;
const bool shaderChanged = shader != oldShader;
bool entityChanged = entityNum != oldEntityNum;

View file

@ -27,6 +27,7 @@ along with Challenge Quake 3. If not, see <https://www.gnu.org/licenses/>.
#define IMAGE_WINDOW_NAME "Image Details"
#define SHADER_WINDOW_NAME "Shader Details"
#define EDIT_WINDOW_NAME "Shader Editor"
struct ImageWindow
@ -67,10 +68,19 @@ struct ImageReplacements
int count;
};
struct ShaderEditWindow
{
char code[4096];
shader_t originalShader;
shader_t* shader;
bool active;
};
static ImageWindow imageWindow;
static ShaderWindow shaderWindow;
static ShaderReplacements shaderReplacements;
static ImageReplacements imageReplacements;
static ShaderEditWindow shaderEditWindow;
static const char* mipNames[16] =
{
@ -109,30 +119,25 @@ static const ImageFlag imageFlags[] =
};
static void TitleText(const char* text)
#if 0
static void SelectableText(const char* text)
{
ImGui::TextColored(ImVec4(1.0f, 0.25f, 0.0f, 1.0f), text);
char buffer[256];
Q_strncpyz(buffer, text, sizeof(buffer));
ImGui::PushID(text);
ImGui::PushItemWidth(ImGui::GetWindowSize().x);
ImGui::InputText("", buffer, sizeof(buffer), ImGuiInputTextFlags_ReadOnly);
ImGui::PopItemWidth();
ImGui::PopID();
}
#endif
static void OpenImageDetails(const image_t* image)
static void FormatShaderCode(char* dest, int destSize, const shader_t* shader)
{
ImGui::SetWindowFocus(IMAGE_WINDOW_NAME);
#define Append(Text) Q_strcat(dest, destSize, Text)
imageWindow.active = true;
imageWindow.image = image;
imageWindow.mip = 0;
}
static void OpenShaderDetails(shader_t* shader)
{
#define Append(Text) Q_strcat(shaderWindow.formattedCode, sizeof(shaderWindow.formattedCode), Text)
ImGui::SetWindowFocus(SHADER_WINDOW_NAME);
shaderWindow.active = true;
shaderWindow.shader = shader;
shaderWindow.formattedCode[0] = '\0';
dest[0] = '\0';
if(shader->text == NULL)
{
return;
@ -176,11 +181,69 @@ static void OpenShaderDetails(shader_t* shader)
#undef Append
}
static bool AreCheatsEnabled()
static void TitleText(const char* text)
{
ImGui::TextColored(ImVec4(1.0f, 0.25f, 0.0f, 1.0f), text);
}
static void OpenImageDetails(const image_t* image)
{
ImGui::SetWindowFocus(IMAGE_WINDOW_NAME);
imageWindow.active = true;
imageWindow.image = image;
imageWindow.mip = 0;
}
static void OpenShaderDetails(shader_t* shader)
{
ImGui::SetWindowFocus(SHADER_WINDOW_NAME);
shaderWindow.active = true;
shaderWindow.shader = shader;
FormatShaderCode(shaderWindow.formattedCode, sizeof(shaderWindow.formattedCode), shader);
}
static void OpenShaderEdit(shader_t* shader)
{
ImGui::SetWindowFocus(EDIT_WINDOW_NAME);
if(shaderEditWindow.active)
{
R_SetShaderData(shaderEditWindow.shader, &shaderEditWindow.originalShader);
}
shaderEditWindow.active = true;
shaderEditWindow.shader = shader;
shaderEditWindow.originalShader = *shader;
FormatShaderCode(shaderEditWindow.code, sizeof(shaderEditWindow.code), shader);
tr.shaderParseFailed = qfalse;
tr.shaderParseNumWarnings = 0;
}
static bool IsCheating()
{
return ri.Cvar_Get("sv_cheats", "0", 0)->integer != 0;
}
static void RemoveShaderReplacement(int shaderIndex)
{
for(int i = 0; i < shaderReplacements.count; ++i)
{
const ShaderReplacement& repl = shaderReplacements.shaders[i];
if(shaderIndex == repl.index && shaderIndex >= 0 && shaderIndex < tr.numShaders)
{
*tr.shaders[repl.index] = repl.original;
if(i < shaderReplacements.count - 1)
{
shaderReplacements.shaders[i] = shaderReplacements.shaders[shaderReplacements.count - 1];
}
shaderReplacements.count--;
break;
}
}
}
static void AddShaderReplacement(int shaderIndex)
{
if(shaderReplacements.count >= ARRAY_LEN(shaderReplacements.shaders))
@ -201,6 +264,13 @@ static void AddShaderReplacement(int shaderIndex)
}
}
ShaderEditWindow& edit = shaderEditWindow;
if(edit.active && edit.shader->index == shaderIndex)
{
RemoveShaderReplacement(shaderIndex);
edit.active = false;
}
ShaderReplacement& repl = shaderReplacements.shaders[shaderReplacements.count++];
repl.index = shaderIndex;
repl.original = *tr.shaders[shaderIndex];
@ -210,24 +280,6 @@ static void AddShaderReplacement(int shaderIndex)
tr.shaders[shaderIndex]->sortedIndex = repl.original.sortedIndex;
}
static void RemoveShaderReplacement(int shaderIndex)
{
for(int i = 0; i < shaderReplacements.count; ++i)
{
const ShaderReplacement& repl = shaderReplacements.shaders[i];
if(shaderIndex == repl.index && shaderIndex >= 0 && shaderIndex < tr.numShaders)
{
*tr.shaders[repl.index] = repl.original;
if(i < shaderReplacements.count - 1)
{
shaderReplacements.shaders[i] = shaderReplacements.shaders[shaderReplacements.count - 1];
}
shaderReplacements.count--;
break;
}
}
}
static bool IsReplacedShader(int shaderIndex)
{
for(int i = 0; i < shaderReplacements.count; ++i)
@ -437,7 +489,7 @@ static void DrawImageWindow()
}
}
if(AreCheatsEnabled())
if(IsCheating())
{
if(IsReplacedImage(image->index))
{
@ -591,9 +643,12 @@ static void DrawShaderWindow()
ImGui::Text(shaderPath);
}
if(AreCheatsEnabled())
const bool isReplaced = IsReplacedShader(shader->index);
const bool isCheating = IsCheating();
if(isCheating)
{
if(IsReplacedShader(shader->index))
if(isReplaced)
{
if(ImGui::Button("Restore Shader"))
{
@ -631,6 +686,9 @@ static void DrawShaderWindow()
}
}
}
const image_t* displayImages[MAX_IMAGE_ANIMATIONS * MAX_SHADER_STAGES];
int displayImageCount = 0;
for(int s = 0; s < shader->numStages; ++s)
{
const textureBundle_t& bundle = shader->stages[s]->bundle;
@ -638,6 +696,25 @@ static void DrawShaderWindow()
for(int i = 0; i < imageCount; ++i)
{
const image_t* image = bundle.image[i];
bool found = false;
for(int di = 0; di < displayImageCount; ++di)
{
if(displayImages[di] == image)
{
found = true;
break;
}
}
if(found)
{
continue;
}
if(displayImageCount < ARRAY_LEN(displayImages))
{
displayImages[displayImageCount++] = image;
}
if(ImGui::Selectable(va("%s##%d_%d", image->name, s, i), false))
{
OpenImageDetails(image);
@ -650,26 +727,131 @@ static void DrawShaderWindow()
}
ImGui::NewLine();
if(window.formattedCode[0] != '\0')
if(isReplaced)
{
ImGui::Text("No code available (using default shader)");
}
else if(shaderEditWindow.active && window.shader == shaderEditWindow.shader)
{
ImGui::Text("Shader code is being edited");
if(shaderEditWindow.code[0] != '\0')
{
ImGui::Text("This is the current edited code, not the last successful compile");
ImGui::TextUnformatted(shaderEditWindow.code);
}
}
else if(window.formattedCode[0] != '\0')
{
ImGui::TextUnformatted(window.formattedCode);
ImGui::NewLine();
if(ImGui::Button("Copy Code"))
{
ImGui::SetClipboardText(window.formattedCode);
}
}
else
{
ImGui::Text("No code available");
}
if(isCheating)
{
if(!shaderEditWindow.active || window.shader != shaderEditWindow.shader)
{
ImGui::NewLine();
if(ImGui::Button("Edit Shader"))
{
if(isReplaced)
{
RemoveShaderReplacement(shader->index);
}
OpenShaderEdit(shader);
}
}
}
}
ImGui::End();
}
}
static int ShaderEditCallback(ImGuiInputTextCallbackData* data)
{
data->InsertChars(data->CursorPos, " ");
return 1;
}
static void DrawShaderEdit()
{
ShaderEditWindow& window = shaderEditWindow;
const bool wasActive = window.active;
if(window.active)
{
if(ImGui::Begin(EDIT_WINDOW_NAME, &window.active, ImGuiWindowFlags_AlwaysAutoResize))
{
shader_t* shader = window.shader;
TitleText(shader->name);
if(IsCheating())
{
ImGui::NewLine();
if(ImGui::IsWindowAppearing())
{
ImGui::SetKeyboardFocusHere();
}
const ImVec2 xSize = ImGui::CalcTextSize("X");
const ImVec2 inputSize = ImVec2(xSize.x * 60.0f, xSize.y * 30.0f);
ImGui::InputTextMultiline(
"##Shader Code", window.code, sizeof(window.code), inputSize,
ImGuiInputTextFlags_CallbackCompletion, &ShaderEditCallback);
ImGui::NewLine();
if(ImGui::Button("Apply Code"))
{
R_EditShader(window.shader, &window.originalShader, window.code);
}
ImGui::SameLine(inputSize.x - ImGui::CalcTextSize("Close and Discard").x);
if(ImGui::Button("Close and Discard"))
{
window.active = false;
}
if(tr.shaderParseFailed || tr.shaderParseNumWarnings > 0)
{
ImGui::NewLine();
}
if(tr.shaderParseFailed)
{
ImGui::TextColored(ImVec4(1.0f, 0.25f, 0.0f, 1.0f), "Error: %s", tr.shaderParseError);
}
for(int i = 0; i < tr.shaderParseNumWarnings; ++i)
{
ImGui::TextColored(ImVec4(1.0f, 0.5f, 0.0f, 1.0f), "Warning: %s", tr.shaderParseWarnings[i]);
}
}
else
{
ImGui::Text("Enable cheats to use");
}
}
ImGui::End();
}
if(wasActive && !window.active)
{
R_SetShaderData(window.shader, &window.originalShader);
}
}
void R_DrawGUI()
{
DrawImageList();
DrawImageWindow();
DrawShaderList();
DrawShaderWindow();
DrawShaderEdit();
}
void R_ShutDownGUI()
@ -677,10 +859,13 @@ void R_ShutDownGUI()
// this is necessary to avoid crashes in the detail windows
// following a map change or video restart:
// the renderer is shut down and the pointers become stale
imageWindow.active = false;
imageWindow.image = NULL;
shaderWindow.active = false;
shaderWindow.shader = NULL;
if(shaderEditWindow.active)
{
R_SetShaderData(shaderEditWindow.shader, &shaderEditWindow.originalShader);
}
memset(&imageWindow, 0, sizeof(imageWindow));
memset(&shaderWindow, 0, sizeof(shaderWindow));
memset(&shaderEditWindow, 0, sizeof(shaderEditWindow));
ClearShaderReplacements();
ClearImageReplacements();
}

View file

@ -866,14 +866,18 @@ typedef struct {
#define FOG_TABLE_SIZE 256
enum renderMode_t
{
enum renderMode_t {
RM_NONE,
RM_UI,
RM_3D
};
struct shaderParseMessage_t {
char message[256];
};
/*
** trGlobals_t
**
@ -964,6 +968,16 @@ typedef struct {
qbool worldSurface; // is the currently added draw surface a world surface?
renderMode_t renderMode;
// the following are only to be used after calling R_UpdateShader()
// the save state boolean is needed because otherwise, a delayed shader load
// could change the error and warning messages of the edited shader
shaderParseMessage_t shaderParseError;
shaderParseMessage_t shaderParseWarnings[16];
qbool shaderParseSaveState;
qbool shaderParseFailed;
int shaderParseNumWarnings;
} trGlobals_t;
extern backEndState_t backEnd;
@ -1236,7 +1250,9 @@ void R_ShaderList_f( void );
void R_ShaderInfo_f();
void R_ShaderMixedUse_f();
void R_CompleteShaderName_f( int startArg, int compArg );
const char* R_GetShaderPath( const shader_t* shader );
const char* R_GetShaderPath( const shader_t* shader );
qbool R_EditShader( shader_t* sh, const shader_t* original, const char* shaderText );
void R_SetShaderData( shader_t* sh, const shader_t* original );
/*
====================================================================

View file

@ -62,6 +62,39 @@ static shader_t* hashTable[FILE_HASH_SIZE];
#define MAX_SHADERTEXT_HASH 2048
static char** shaderTextHashTable[MAX_SHADERTEXT_HASH];
static char parseMessage[1024];
static void ParserWarning( PRINTF_FORMAT_STRING const char* format, ... )
{
va_list ap;
va_start( ap, format );
Q_vsnprintf( parseMessage, sizeof( parseMessage ) - 1, format, ap );
va_end( ap );
ri.Printf( PRINT_WARNING, "WARNING: %s in shader '%s'\n", parseMessage, shader.name );
if ( tr.shaderParseSaveState ) {
if ( tr.shaderParseNumWarnings < ARRAY_LEN( tr.shaderParseWarnings ) ) {
Q_strncpyz( tr.shaderParseWarnings[tr.shaderParseNumWarnings++].message, parseMessage, sizeof( tr.shaderParseWarnings[0] ) );
}
}
}
static void ParserError( PRINTF_FORMAT_STRING const char* format, ... )
{
va_list ap;
va_start( ap, format );
Q_vsnprintf( parseMessage, sizeof( parseMessage ) - 1, format, ap );
va_end( ap );
ri.Printf( PRINT_WARNING, "ERROR: %s in shader '%s'\n", parseMessage, shader.name );
if ( tr.shaderParseSaveState ) {
Q_strncpyz( tr.shaderParseError.message, parseMessage, sizeof( tr.shaderParseError.message ) );
}
}
static qbool ParseVector( const char** text, int count, float *v )
{
@ -70,14 +103,14 @@ static qbool ParseVector( const char** text, int count, float *v )
// FIXME: spaces are currently required after parens, should change parseext...
const char* token = COM_ParseExt( text, qfalse );
if ( strcmp( token, "(" ) ) {
ri.Printf( PRINT_WARNING, "WARNING: missing parenthesis in shader '%s'\n", shader.name );
ParserWarning( "missing parenthesis" );
return qfalse;
}
for ( i = 0 ; i < count ; i++ ) {
token = COM_ParseExt( text, qfalse );
if ( !token[0] ) {
ri.Printf( PRINT_WARNING, "WARNING: missing vector element in shader '%s'\n", shader.name );
ParserWarning( "missing vector element" );
return qfalse;
}
v[i] = atof( token );
@ -85,7 +118,7 @@ static qbool ParseVector( const char** text, int count, float *v )
token = COM_ParseExt( text, qfalse );
if ( strcmp( token, ")" ) ) {
ri.Printf( PRINT_WARNING, "WARNING: missing parenthesis in shader '%s'\n", shader.name );
ParserWarning( "missing parenthesis" );
return qfalse;
}
@ -113,7 +146,7 @@ static unsigned NameToAFunc( const char *funcname )
return GLS_ATEST_GE_80;
}
ri.Printf( PRINT_WARNING, "WARNING: invalid alphaFunc name '%s' in shader '%s'\n", funcname, shader.name );
ParserWarning( "invalid alphaFunc name '%s'", funcname );
return 0;
}
@ -162,7 +195,7 @@ static int NameToSrcBlendMode( const char *name )
return GLS_SRCBLEND_ALPHA_SATURATE;
}
ri.Printf( PRINT_WARNING, "WARNING: unknown blend mode '%s' in shader '%s', substituting GL_ONE\n", name, shader.name );
ParserWarning( "unknown blend mode '%s', using GL_ONE instead", name );
return GLS_SRCBLEND_ONE;
}
@ -206,7 +239,7 @@ static int NameToDstBlendMode( const char *name )
return GLS_DSTBLEND_ONE_MINUS_SRC_COLOR;
}
ri.Printf( PRINT_WARNING, "WARNING: unknown blend mode '%s' in shader '%s', substituting GL_ONE\n", name, shader.name );
ParserWarning( "unknown blend mode '%s', using GL_ONE instead", name );
return GLS_DSTBLEND_ONE;
}
@ -242,7 +275,7 @@ static genFunc_t NameToGenFunc( const char *funcname )
return GF_NOISE;
}
ri.Printf( PRINT_WARNING, "WARNING: invalid genfunc name '%s' in shader '%s'\n", funcname, shader.name );
ParserWarning( "invalid genfunc name '%s'", funcname );
return GF_SIN;
}
@ -252,7 +285,7 @@ static void ParseWaveForm( const char** text, waveForm_t* wave )
const char* token = COM_ParseExt( text, qfalse );
if ( token[0] == 0 )
{
ri.Printf( PRINT_WARNING, "WARNING: missing waveform parm in shader '%s'\n", shader.name );
ParserWarning( "missing waveform parm" );
return;
}
wave->func = NameToGenFunc( token );
@ -261,7 +294,7 @@ static void ParseWaveForm( const char** text, waveForm_t* wave )
token = COM_ParseExt( text, qfalse );
if ( token[0] == 0 )
{
ri.Printf( PRINT_WARNING, "WARNING: missing waveform parm in shader '%s'\n", shader.name );
ParserWarning( "missing waveform parm" );
return;
}
wave->base = atof( token );
@ -269,7 +302,7 @@ static void ParseWaveForm( const char** text, waveForm_t* wave )
token = COM_ParseExt( text, qfalse );
if ( token[0] == 0 )
{
ri.Printf( PRINT_WARNING, "WARNING: missing waveform parm in shader '%s'\n", shader.name );
ParserWarning( "missing waveform parm" );
return;
}
wave->amplitude = atof( token );
@ -277,7 +310,7 @@ static void ParseWaveForm( const char** text, waveForm_t* wave )
token = COM_ParseExt( text, qfalse );
if ( token[0] == 0 )
{
ri.Printf( PRINT_WARNING, "WARNING: missing waveform parm in shader '%s'\n", shader.name );
ParserWarning( "missing waveform parm" );
return;
}
wave->phase = atof( token );
@ -285,7 +318,7 @@ static void ParseWaveForm( const char** text, waveForm_t* wave )
token = COM_ParseExt( text, qfalse );
if ( token[0] == 0 )
{
ri.Printf( PRINT_WARNING, "WARNING: missing waveform parm in shader '%s'\n", shader.name );
ParserWarning( "missing waveform parm" );
return;
}
wave->frequency = atof( token );
@ -298,7 +331,7 @@ static void ParseTexMod( const char** text, shaderStage_t *stage )
texModInfo_t *tmi;
if ( stage->numTexMods == TR_MAX_TEXMODS ) {
ri.Error( ERR_DROP, "ERROR: too many tcMod stages in shader '%s'\n", shader.name );
ParserError( "too many tcMod stages" );
return;
}
@ -315,28 +348,28 @@ static void ParseTexMod( const char** text, shaderStage_t *stage )
token = COM_ParseExt( text, qfalse );
if ( token[0] == 0 )
{
ri.Printf( PRINT_WARNING, "WARNING: missing tcMod turb parms in shader '%s'\n", shader.name );
ParserWarning( "missing tcMod turb parms" );
return;
}
tmi->wave.base = atof( token );
token = COM_ParseExt( text, qfalse );
if ( token[0] == 0 )
{
ri.Printf( PRINT_WARNING, "WARNING: missing tcMod turb in shader '%s'\n", shader.name );
ParserWarning( "missing tcMod turb parms" );
return;
}
tmi->wave.amplitude = atof( token );
token = COM_ParseExt( text, qfalse );
if ( token[0] == 0 )
{
ri.Printf( PRINT_WARNING, "WARNING: missing tcMod turb in shader '%s'\n", shader.name );
ParserWarning( "missing tcMod turb parms" );
return;
}
tmi->wave.phase = atof( token );
token = COM_ParseExt( text, qfalse );
if ( token[0] == 0 )
{
ri.Printf( PRINT_WARNING, "WARNING: missing tcMod turb in shader '%s'\n", shader.name );
ParserWarning( "missing tcMod turb parms" );
return;
}
tmi->wave.frequency = atof( token );
@ -351,7 +384,7 @@ static void ParseTexMod( const char** text, shaderStage_t *stage )
token = COM_ParseExt( text, qfalse );
if ( token[0] == 0 )
{
ri.Printf( PRINT_WARNING, "WARNING: missing scale parms in shader '%s'\n", shader.name );
ParserWarning( "missing scale parms" );
return;
}
tmi->scale[0] = atof( token );
@ -359,7 +392,7 @@ static void ParseTexMod( const char** text, shaderStage_t *stage )
token = COM_ParseExt( text, qfalse );
if ( token[0] == 0 )
{
ri.Printf( PRINT_WARNING, "WARNING: missing scale parms in shader '%s'\n", shader.name );
ParserWarning( "missing scale parms" );
return;
}
tmi->scale[1] = atof( token );
@ -373,14 +406,14 @@ static void ParseTexMod( const char** text, shaderStage_t *stage )
token = COM_ParseExt( text, qfalse );
if ( token[0] == 0 )
{
ri.Printf( PRINT_WARNING, "WARNING: missing scale scroll parms in shader '%s'\n", shader.name );
ParserWarning( "missing scale scroll parms" );
return;
}
tmi->scroll[0] = atof( token );
token = COM_ParseExt( text, qfalse );
if ( token[0] == 0 )
{
ri.Printf( PRINT_WARNING, "WARNING: missing scale scroll parms in shader '%s'\n", shader.name );
ParserWarning( "missing scale scroll parms" );
return;
}
tmi->scroll[1] = atof( token );
@ -394,7 +427,7 @@ static void ParseTexMod( const char** text, shaderStage_t *stage )
token = COM_ParseExt( text, qfalse );
if ( token[0] == 0 )
{
ri.Printf( PRINT_WARNING, "WARNING: missing stretch parms in shader '%s'\n", shader.name );
ParserWarning( "missing stretch parms" );
return;
}
tmi->wave.func = NameToGenFunc( token );
@ -402,7 +435,7 @@ static void ParseTexMod( const char** text, shaderStage_t *stage )
token = COM_ParseExt( text, qfalse );
if ( token[0] == 0 )
{
ri.Printf( PRINT_WARNING, "WARNING: missing stretch parms in shader '%s'\n", shader.name );
ParserWarning( "missing stretch parms" );
return;
}
tmi->wave.base = atof( token );
@ -410,7 +443,7 @@ static void ParseTexMod( const char** text, shaderStage_t *stage )
token = COM_ParseExt( text, qfalse );
if ( token[0] == 0 )
{
ri.Printf( PRINT_WARNING, "WARNING: missing stretch parms in shader '%s'\n", shader.name );
ParserWarning( "missing stretch parms" );
return;
}
tmi->wave.amplitude = atof( token );
@ -418,7 +451,7 @@ static void ParseTexMod( const char** text, shaderStage_t *stage )
token = COM_ParseExt( text, qfalse );
if ( token[0] == 0 )
{
ri.Printf( PRINT_WARNING, "WARNING: missing stretch parms in shader '%s'\n", shader.name );
ParserWarning( "missing stretch parms" );
return;
}
tmi->wave.phase = atof( token );
@ -426,7 +459,7 @@ static void ParseTexMod( const char** text, shaderStage_t *stage )
token = COM_ParseExt( text, qfalse );
if ( token[0] == 0 )
{
ri.Printf( PRINT_WARNING, "WARNING: missing stretch parms in shader '%s'\n", shader.name );
ParserWarning( "missing stretch parms" );
return;
}
tmi->wave.frequency = atof( token );
@ -441,7 +474,7 @@ static void ParseTexMod( const char** text, shaderStage_t *stage )
token = COM_ParseExt( text, qfalse );
if ( token[0] == 0 )
{
ri.Printf( PRINT_WARNING, "WARNING: missing transform parms in shader '%s'\n", shader.name );
ParserWarning( "missing transform parms" );
return;
}
tmi->matrix[0][0] = atof( token );
@ -449,7 +482,7 @@ static void ParseTexMod( const char** text, shaderStage_t *stage )
token = COM_ParseExt( text, qfalse );
if ( token[0] == 0 )
{
ri.Printf( PRINT_WARNING, "WARNING: missing transform parms in shader '%s'\n", shader.name );
ParserWarning( "missing transform parms" );
return;
}
tmi->matrix[0][1] = atof( token );
@ -457,7 +490,7 @@ static void ParseTexMod( const char** text, shaderStage_t *stage )
token = COM_ParseExt( text, qfalse );
if ( token[0] == 0 )
{
ri.Printf( PRINT_WARNING, "WARNING: missing transform parms in shader '%s'\n", shader.name );
ParserWarning( "missing transform parms" );
return;
}
tmi->matrix[1][0] = atof( token );
@ -465,7 +498,7 @@ static void ParseTexMod( const char** text, shaderStage_t *stage )
token = COM_ParseExt( text, qfalse );
if ( token[0] == 0 )
{
ri.Printf( PRINT_WARNING, "WARNING: missing transform parms in shader '%s'\n", shader.name );
ParserWarning( "missing transform parms" );
return;
}
tmi->matrix[1][1] = atof( token );
@ -473,7 +506,7 @@ static void ParseTexMod( const char** text, shaderStage_t *stage )
token = COM_ParseExt( text, qfalse );
if ( token[0] == 0 )
{
ri.Printf( PRINT_WARNING, "WARNING: missing transform parms in shader '%s'\n", shader.name );
ParserWarning( "missing transform parms" );
return;
}
tmi->translate[0] = atof( token );
@ -481,7 +514,7 @@ static void ParseTexMod( const char** text, shaderStage_t *stage )
token = COM_ParseExt( text, qfalse );
if ( token[0] == 0 )
{
ri.Printf( PRINT_WARNING, "WARNING: missing transform parms in shader '%s'\n", shader.name );
ParserWarning( "missing transform parms" );
return;
}
tmi->translate[1] = atof( token );
@ -496,7 +529,7 @@ static void ParseTexMod( const char** text, shaderStage_t *stage )
token = COM_ParseExt( text, qfalse );
if ( token[0] == 0 )
{
ri.Printf( PRINT_WARNING, "WARNING: missing tcMod rotate parms in shader '%s'\n", shader.name );
ParserWarning( "missing tcMod rotate parms" );
return;
}
tmi->rotateSpeed = atof( token );
@ -511,7 +544,7 @@ static void ParseTexMod( const char** text, shaderStage_t *stage )
}
else
{
ri.Printf( PRINT_WARNING, "WARNING: unknown tcMod '%s' in shader '%s'\n", token, shader.name );
ParserWarning( "unknown tcMod '%s'", token );
}
}
@ -529,7 +562,7 @@ static qbool ParseStage( const char** text, shaderStage_t* stage )
token = COM_ParseExt( text, qtrue );
if ( !token[0] )
{
ri.Printf( PRINT_WARNING, "WARNING: no matching '}' found\n" );
ParserError( "no matching '}' found" );
return qfalse;
}
@ -545,7 +578,7 @@ static qbool ParseStage( const char** text, shaderStage_t* stage )
token = COM_ParseExt( text, qfalse );
if ( !token[0] )
{
ri.Printf( PRINT_WARNING, "WARNING: missing parameter for 'map' keyword in shader '%s'\n", shader.name );
ParserError( "missing parameter for 'map' keyword" );
return qfalse;
}
@ -578,7 +611,7 @@ static qbool ParseStage( const char** text, shaderStage_t* stage )
stage->bundle.image[0] = R_FindImageFile( token, shader.imgflags, TW_REPEAT );
if ( !stage->bundle.image[0] )
{
ri.Printf( PRINT_WARNING, "WARNING: R_FindImageFile could not find '%s' in shader '%s'\n", token, shader.name );
ParserError( "R_FindImageFile could not find '%s'", token );
return qfalse;
}
}
@ -591,14 +624,14 @@ static qbool ParseStage( const char** text, shaderStage_t* stage )
token = COM_ParseExt( text, qfalse );
if ( !token[0] )
{
ri.Printf( PRINT_WARNING, "WARNING: missing parameter for 'clampmap' keyword in shader '%s'\n", shader.name );
ParserError( "missing parameter for 'clampmap' keyword" );
return qfalse;
}
stage->bundle.image[0] = R_FindImageFile( token, shader.imgflags, TW_CLAMP_TO_EDGE );
if ( !stage->bundle.image[0] )
{
ri.Printf( PRINT_WARNING, "WARNING: R_FindImageFile could not find '%s' in shader '%s'\n", token, shader.name );
ParserError( "R_FindImageFile could not find '%s'", token );
return qfalse;
}
}
@ -610,7 +643,7 @@ static qbool ParseStage( const char** text, shaderStage_t* stage )
token = COM_ParseExt( text, qfalse );
if ( !token[0] )
{
ri.Printf( PRINT_WARNING, "WARNING: missing parameter for 'animMmap' keyword in shader '%s'\n", shader.name );
ParserError( "missing parameter for 'animMmap' keyword" );
return qfalse;
}
stage->bundle.imageAnimationSpeed = atof( token );
@ -626,7 +659,7 @@ static qbool ParseStage( const char** text, shaderStage_t* stage )
stage->bundle.image[num] = R_FindImageFile( token, shader.imgflags, TW_REPEAT );
if ( !stage->bundle.image[num] )
{
ri.Printf( PRINT_WARNING, "WARNING: R_FindImageFile could not find '%s' in shader '%s'\n", token, shader.name );
ParserError( "R_FindImageFile could not find '%s'", token );
return qfalse;
}
stage->bundle.numImageAnimations++;
@ -638,7 +671,7 @@ static qbool ParseStage( const char** text, shaderStage_t* stage )
token = COM_ParseExt( text, qfalse );
if ( !token[0] )
{
ri.Printf( PRINT_WARNING, "WARNING: missing parameter for 'videoMap' keyword in shader '%s'\n", shader.name );
ParserError( "missing parameter for 'videoMap' keyword" );
return qfalse;
}
stage->bundle.videoMapHandle = ri.CIN_PlayCinematic( token, 0, 0, 256, 256, (CIN_loop | CIN_silent | CIN_shader));
@ -655,7 +688,7 @@ static qbool ParseStage( const char** text, shaderStage_t* stage )
token = COM_ParseExt( text, qfalse );
if ( !token[0] )
{
ri.Printf( PRINT_WARNING, "WARNING: missing parameter for 'alphaFunc' keyword in shader '%s'\n", shader.name );
ParserError( "missing parameter for 'alphaFunc' keyword" );
return qfalse;
}
@ -670,7 +703,7 @@ static qbool ParseStage( const char** text, shaderStage_t* stage )
if ( !token[0] )
{
ri.Printf( PRINT_WARNING, "WARNING: missing parameter for 'depthfunc' keyword in shader '%s'\n", shader.name );
ParserError( "missing parameter for 'depthFunc' keyword" );
return qfalse;
}
@ -684,7 +717,7 @@ static qbool ParseStage( const char** text, shaderStage_t* stage )
}
else
{
ri.Printf( PRINT_WARNING, "WARNING: unknown depthfunc '%s' in shader '%s'\n", token, shader.name );
ParserWarning( "unknown depthFunc '%s'", token );
continue;
}
}
@ -704,7 +737,7 @@ static qbool ParseStage( const char** text, shaderStage_t* stage )
token = COM_ParseExt( text, qfalse );
if ( token[0] == 0 )
{
ri.Printf( PRINT_WARNING, "WARNING: missing parm for blendFunc in shader '%s'\n", shader.name );
ParserWarning( "missing parm for blendFunc" );
continue;
}
// check for "simple" blends first
@ -724,7 +757,7 @@ static qbool ParseStage( const char** text, shaderStage_t* stage )
token = COM_ParseExt( text, qfalse );
if ( token[0] == 0 )
{
ri.Printf( PRINT_WARNING, "WARNING: missing parm for blendFunc in shader '%s'\n", shader.name );
ParserWarning( "missing parm for blendFunc" );
continue;
}
blendDstBits = NameToDstBlendMode( token );
@ -744,7 +777,7 @@ static qbool ParseStage( const char** text, shaderStage_t* stage )
token = COM_ParseExt( text, qfalse );
if ( token[0] == 0 )
{
ri.Printf( PRINT_WARNING, "WARNING: missing parameters for rgbGen in shader '%s'\n", shader.name );
ParserWarning( "missing parameters for rgbGen" );
continue;
}
@ -799,7 +832,7 @@ static qbool ParseStage( const char** text, shaderStage_t* stage )
}
else
{
ri.Printf( PRINT_WARNING, "WARNING: unknown rgbGen parameter '%s' in shader '%s'\n", token, shader.name );
ParserWarning( "unknown rgbGen parameter '%s'", token );
continue;
}
}
@ -811,7 +844,7 @@ static qbool ParseStage( const char** text, shaderStage_t* stage )
token = COM_ParseExt( text, qfalse );
if ( token[0] == 0 )
{
ri.Printf( PRINT_WARNING, "WARNING: missing parameters for alphaGen in shader '%s'\n", shader.name );
ParserWarning( "missing parameters for alphaGen" );
continue;
}
@ -857,7 +890,7 @@ static qbool ParseStage( const char** text, shaderStage_t* stage )
if ( token[0] == 0 )
{
shader.portalRange = 256;
ri.Printf( PRINT_WARNING, "WARNING: missing range parameter for alphaGen portal in shader '%s', defaulting to 256\n", shader.name );
ParserWarning( "missing range parameter for alphaGen portal, defaulting to 256" );
}
else
{
@ -866,7 +899,7 @@ static qbool ParseStage( const char** text, shaderStage_t* stage )
}
else
{
ri.Printf( PRINT_WARNING, "WARNING: unknown alphaGen parameter '%s' in shader '%s'\n", token, shader.name );
ParserWarning( "unknown alphaGen parameter '%s'", token );
continue;
}
}
@ -878,7 +911,7 @@ static qbool ParseStage( const char** text, shaderStage_t* stage )
token = COM_ParseExt( text, qfalse );
if ( token[0] == 0 )
{
ri.Printf( PRINT_WARNING, "WARNING: missing texgen parm in shader '%s'\n", shader.name );
ParserWarning( "missing tcGen parm" );
continue;
}
@ -902,7 +935,7 @@ static qbool ParseStage( const char** text, shaderStage_t* stage )
}
else
{
ri.Printf( PRINT_WARNING, "WARNING: unknown texgen parm in shader '%s'\n", shader.name );
ParserWarning( "unknown tcGen parm '%s'", token );
}
}
//
@ -924,7 +957,7 @@ static qbool ParseStage( const char** text, shaderStage_t* stage )
}
else
{
ri.Printf( PRINT_WARNING, "WARNING: unknown parameter '%s' in shader '%s'\n", token, shader.name );
ParserError( "unknown parameter '%s'", token );
return qfalse;
}
}
@ -992,12 +1025,12 @@ static void ParseDeform( const char** text )
token = COM_ParseExt( text, qfalse );
if ( token[0] == 0 )
{
ri.Printf( PRINT_WARNING, "WARNING: missing deform parm in shader '%s'\n", shader.name );
ParserWarning( "missing deform parm" );
return;
}
if ( shader.numDeforms == MAX_SHADER_DEFORMS ) {
ri.Printf( PRINT_WARNING, "WARNING: MAX_SHADER_DEFORMS in '%s'\n", shader.name );
ParserWarning( "too many deforms" );
return;
}
@ -1029,7 +1062,7 @@ static void ParseDeform( const char** text )
token = COM_ParseExt( text, qfalse );
if ( token[0] == 0 )
{
ri.Printf( PRINT_WARNING, "WARNING: missing deformVertexes bulge parm in shader '%s'\n", shader.name );
ParserWarning( "missing deformVertexes bulge parm" );
return;
}
ds->bulgeWidth = atof( token );
@ -1037,7 +1070,7 @@ static void ParseDeform( const char** text )
token = COM_ParseExt( text, qfalse );
if ( token[0] == 0 )
{
ri.Printf( PRINT_WARNING, "WARNING: missing deformVertexes bulge parm in shader '%s'\n", shader.name );
ParserWarning( "missing deformVertexes bulge parm" );
return;
}
ds->bulgeHeight = atof( token );
@ -1045,7 +1078,7 @@ static void ParseDeform( const char** text )
token = COM_ParseExt( text, qfalse );
if ( token[0] == 0 )
{
ri.Printf( PRINT_WARNING, "WARNING: missing deformVertexes bulge parm in shader '%s'\n", shader.name );
ParserWarning( "missing deformVertexes bulge parm" );
return;
}
ds->bulgeSpeed = atof( token );
@ -1059,7 +1092,7 @@ static void ParseDeform( const char** text )
token = COM_ParseExt( text, qfalse );
if ( token[0] == 0 )
{
ri.Printf( PRINT_WARNING, "WARNING: missing deformVertexes parm in shader '%s'\n", shader.name );
ParserWarning( "missing deformVertexes parm" );
return;
}
@ -1070,7 +1103,7 @@ static void ParseDeform( const char** text )
else
{
ds->deformationSpread = 100.0f;
ri.Printf( PRINT_WARNING, "WARNING: illegal div value of 0 in deformVertexes command for shader '%s'\n", shader.name );
ParserWarning( "illegal div value of 0 in deformVertexes" );
}
ParseWaveForm( text, &ds->deformationWave );
@ -1083,7 +1116,7 @@ static void ParseDeform( const char** text )
token = COM_ParseExt( text, qfalse );
if ( token[0] == 0 )
{
ri.Printf( PRINT_WARNING, "WARNING: missing deformVertexes parm in shader '%s'\n", shader.name );
ParserWarning( "missing deformVertexes parm" );
return;
}
ds->deformationWave.amplitude = atof( token );
@ -1091,7 +1124,7 @@ static void ParseDeform( const char** text )
token = COM_ParseExt( text, qfalse );
if ( token[0] == 0 )
{
ri.Printf( PRINT_WARNING, "WARNING: missing deformVertexes parm in shader '%s'\n", shader.name );
ParserWarning( "missing deformVertexes parm" );
return;
}
ds->deformationWave.frequency = atof( token );
@ -1106,7 +1139,7 @@ static void ParseDeform( const char** text )
for ( i = 0 ; i < 3 ; i++ ) {
token = COM_ParseExt( text, qfalse );
if ( token[0] == 0 ) {
ri.Printf( PRINT_WARNING, "WARNING: missing deformVertexes parm in shader '%s'\n", shader.name );
ParserWarning( "missing deformVertexes parm" );
return;
}
ds->moveVector[i] = atof( token );
@ -1117,8 +1150,8 @@ static void ParseDeform( const char** text )
return;
}
//ri.Printf( PRINT_WARNING, "WARNING: unknown deformVertexes subtype '%s' found in shader '%s'\n", token, shader.name );
ri.Error( ERR_FATAL, "unknown deformVertexes subtype '%s' found in shader '%s'\n", token, shader.name );
ParserWarning( "unknown deformVertexes subtype '%s'", token );
shader.numDeforms--;
}
@ -1134,7 +1167,7 @@ static void ParseSkyParms( const char** text )
// outerbox
token = COM_ParseExt( text, qfalse );
if ( token[0] == 0 ) {
ri.Printf( PRINT_WARNING, "WARNING: 'skyParms' missing parameter in shader '%s'\n", shader.name );
ParserWarning( "'skyParms' missing parameter" );
return;
}
if ( strcmp( token, "-" ) ) {
@ -1150,7 +1183,7 @@ static void ParseSkyParms( const char** text )
// cloudheight
token = COM_ParseExt( text, qfalse );
if ( token[0] == 0 ) {
ri.Printf( PRINT_WARNING, "WARNING: 'skyParms' missing parameter in shader '%s'\n", shader.name );
ParserWarning( "'skyParms' missing parameter" );
return;
}
shader.sky.cloudHeight = atof( token );
@ -1162,7 +1195,7 @@ static void ParseSkyParms( const char** text )
// innerbox
token = COM_ParseExt( text, qfalse );
if ( token[0] == 0 ) {
ri.Printf( PRINT_WARNING, "WARNING: 'skyParms' missing parameter in shader '%s'\n", shader.name );
ParserWarning( "'skyParms' missing parameter" );
return;
}
if ( strcmp( token, "-" ) ) {
@ -1186,7 +1219,7 @@ static void ParseSort( const char** text )
token = COM_ParseExt( text, qfalse );
if ( token[0] == 0 ) {
ri.Printf( PRINT_WARNING, "WARNING: missing sort parameter in shader '%s'\n", shader.name );
ParserWarning( "missing sort parameter" );
return;
}
@ -1297,20 +1330,14 @@ static void ParseDepthFade( const char** text )
const char* token = COM_ParseExt( text, qfalse );
float scale;
if ( token[0] == '\0' || sscanf( token, "%f", &scale ) != 1 || scale <= 0.0f ) {
ri.Printf( PRINT_WARNING,
"WARNING: invalid/missing depth fade scale argument '%s' in shader '%s'! "
"Ignoring the directive completely.\n",
token, shader.name );
ParserWarning( "invalid/missing depth fade scale argument '%s'", token );
return;
}
token = COM_ParseExt( text, qfalse );
float bias;
if ( token[0] == '\0' || sscanf( token, "%f", &bias ) != 1 ) {
ri.Printf( PRINT_WARNING,
"WARNING: invalid/missing depth fade bias argument '%s' in shader '%s'! "
"Ignoring the directive completely.\n",
token, shader.name );
ParserWarning( "invalid/missing depth fade bias argument '%s'", token );
return;
}
@ -1331,7 +1358,7 @@ static qbool ParseShader( const char** text )
token = COM_ParseExt( text, qtrue );
if ( token[0] != '{' )
{
ri.Printf( PRINT_WARNING, "WARNING: expecting '{', found '%s' instead in shader '%s'\n", token, shader.name );
ParserError( "expecting '{', found '%s' instead", token );
return qfalse;
}
@ -1340,7 +1367,7 @@ static qbool ParseShader( const char** text )
token = COM_ParseExt( text, qtrue );
if ( !token[0] )
{
ri.Printf( PRINT_WARNING, "WARNING: no concluding '}' in shader %s\n", shader.name );
ParserError( "no concluding '}'" );
return qfalse;
}
@ -1353,7 +1380,7 @@ static qbool ParseShader( const char** text )
else if ( token[0] == '{' )
{
if ( s >= MAX_SHADER_STAGES ) {
ri.Error( ERR_DROP, "too many stages in shader %s\n", shader.name );
ParserError( "too many stages" );
return qfalse;
}
@ -1430,13 +1457,14 @@ static qbool ParseShader( const char** text )
else if ( !Q_stricmp( token, "fogParms" ) )
{
if ( !ParseVector( text, 3, shader.fogParms.color ) ) {
ParserError( "invalid fogParms vector" );
return qfalse;
}
token = COM_ParseExt( text, qfalse );
if ( !token[0] )
{
ri.Printf( PRINT_WARNING, "WARNING: missing parm for 'fogParms' keyword in shader '%s'\n", shader.name );
ParserWarning( "missing parm for 'fogParms' keyword" );
continue;
}
shader.fogParms.depthForOpaque = atof( token );
@ -1469,7 +1497,7 @@ static qbool ParseShader( const char** text )
token = COM_ParseExt( text, qfalse );
if ( token[0] == 0 )
{
ri.Printf( PRINT_WARNING, "WARNING: missing cull parms in shader '%s'\n", shader.name );
ParserWarning( "missing cull parms" );
continue;
}
@ -1483,7 +1511,7 @@ static qbool ParseShader( const char** text )
}
else
{
ri.Printf( PRINT_WARNING, "WARNING: invalid cull parm '%s' in shader '%s'\n", token, shader.name );
ParserWarning( "invalid cull parm '%s'", token );
}
continue;
}
@ -1495,7 +1523,7 @@ static qbool ParseShader( const char** text )
}
else
{
ri.Printf( PRINT_WARNING, "WARNING: unknown general shader parameter '%s' in '%s'\n", token, shader.name );
ParserError( "unknown general shader parameter '%s'", token );
return qfalse;
}
}
@ -1504,6 +1532,7 @@ static qbool ParseShader( const char** text )
// ignore shaders that don't have any stages, unless it is a sky or fog
//
if ( s == 0 && !shader.isSky && !(shader.contentFlags & CONTENTS_FOG ) ) {
ParserError( "non-sky/fog shaders must have at least 1 stage" );
return qfalse;
}
@ -1513,7 +1542,7 @@ static qbool ParseShader( const char** text )
}
static int R_CompareShaders( const void* aPtr, const void* bPtr )
static int CompareShaders( const void* aPtr, const void* bPtr )
{
const shader_t* const a = *(const shader_t**)aPtr;
const shader_t* const b = *(const shader_t**)bPtr;
@ -1529,6 +1558,15 @@ static int R_CompareShaders( const void* aPtr, const void* bPtr )
}
static void SortShaders()
{
qsort( tr.sortedShaders, tr.numShaders, sizeof(shader_t*), &CompareShaders );
for ( int i = 0; i < tr.numShaders; ++i ) {
tr.sortedShaders[i]->sortedIndex = i;
}
}
static qbool IsColorGenDynamic(colorGen_t cGen)
{
switch(cGen)
@ -1746,10 +1784,7 @@ static void SortNewShader()
tr.sortedShaders[i+1] = newShader;
// sort it more aggressively for better performance
qsort( tr.sortedShaders, tr.numShaders, sizeof(shader_t*), &R_CompareShaders );
for ( i = 0; i < tr.numShaders; ++i ) {
tr.sortedShaders[i]->sortedIndex = i;
}
SortShaders();
//
// If we register a new shader when surfaces are already added,
@ -1780,15 +1815,33 @@ static void SortNewShader()
}
static shader_t* GeneratePermanentShader()
static shader_t* GeneratePermanentShader( shader_t* sh )
{
if ( tr.numShaders == MAX_SHADERS ) {
ri.Printf( PRINT_WARNING, "WARNING: GeneratePermanentShader - MAX_SHADERS hit\n");
ri.Printf( PRINT_WARNING, "WARNING: GeneratePermanentShader - MAX_SHADERS hit\n" );
return tr.defaultShader;
}
shader_t* newShader = RI_New<shader_t>();
*newShader = shader;
shader_t* newShader;
if ( sh != NULL ) {
newShader = sh;
*newShader = shader;
} else {
newShader = RI_New<shader_t>();
*newShader = shader;
tr.shaders[tr.numShaders] = newShader;
newShader->index = tr.numShaders;
tr.sortedShaders[tr.numShaders] = newShader;
newShader->sortedIndex = tr.numShaders;
tr.numShaders++;
const int hash = Q_FileHash( newShader->name, FILE_HASH_SIZE );
newShader->next = hashTable[hash];
hashTable[hash] = newShader;
}
if ( shader.sort <= SS_OPAQUE ) {
newShader->fogPass = FP_EQUAL;
@ -1796,14 +1849,6 @@ static shader_t* GeneratePermanentShader()
newShader->fogPass = FP_LE;
}
tr.shaders[ tr.numShaders ] = newShader;
newShader->index = tr.numShaders;
tr.sortedShaders[ tr.numShaders ] = newShader;
newShader->sortedIndex = tr.numShaders;
tr.numShaders++;
for ( int i = 0; i < newShader->numStages; ++i ) {
if ( !stages[i].active ) {
newShader->numStages = i;
@ -1819,14 +1864,14 @@ static shader_t* GeneratePermanentShader()
ClassifyShader( newShader );
SortNewShader();
if ( sh != NULL ) {
SortShaders();
} else {
SortNewShader();
}
renderPipeline->ProcessShader( *newShader );
int hash = Q_FileHash(newShader->name, FILE_HASH_SIZE);
newShader->next = hashTable[hash];
hashTable[hash] = newShader;
return newShader;
}
@ -2347,7 +2392,7 @@ static void BuildPerImageShaderList( shader_t* newShader )
// returns a freshly allocated shader with info
// copied from the current global working shader
static shader_t* FinishShader()
static shader_t* FinishShader( shader_t* sh = NULL )
{
int stage;
@ -2370,7 +2415,7 @@ static shader_t* FinishShader()
// check for a missing texture
if ( !pStage->bundle.image[0] ) {
ri.Printf( PRINT_WARNING, "Shader %s has a stage with no image\n", shader.name );
ParserWarning( "found a stage with no image" );
pStage->active = qfalse;
continue;
}
@ -2540,7 +2585,7 @@ static shader_t* FinishShader()
FixUnusedBlendModes();
shader_t* const newShader = GeneratePermanentShader();
shader_t* const newShader = GeneratePermanentShader( sh );
BuildPerImageShaderList( newShader );
@ -2573,6 +2618,46 @@ static const char* FindShaderInShaderText( const char* shadername )
}
qbool R_EditShader( shader_t* sh, const shader_t* original, const char* shaderText )
{
Com_Memset( &shader, 0, sizeof( shader ) );
Q_strncpyz( shader.name, original->name, sizeof( shader.name ) ); // for console messages
Com_Memset( &stages, 0, sizeof( stages ) );
for ( int i = 0; i < MAX_SHADER_STAGES; i++ ) {
stages[i].texMods = texMods[i];
}
tr.shaderParseSaveState = qtrue;
tr.shaderParseNumWarnings = 0;
tr.shaderParseFailed = !ParseShader( &shaderText );
tr.shaderParseSaveState = qfalse;
if ( tr.shaderParseFailed ) {
*sh = *original;
SortShaders();
return qfalse;
}
FinishShader( sh );
Q_strncpyz( sh->name, original->name, sizeof( sh->name ) );
sh->index = original->index;
sh->lightmapIndex = original->lightmapIndex;
sh->text = original->text;
sh->next = original->next;
sh->isDynamic = true;
return qtrue;
}
void R_SetShaderData( shader_t* sh, const shader_t* original )
{
*sh = *original;
SortShaders();
}
/*
===============
R_FindShader
@ -2780,7 +2865,7 @@ qhandle_t RE_RegisterShaderFromImage( const char* name, image_t* image )
static qhandle_t RE_RegisterShaderInternal( const char* name, int lightmapIndex, qbool mip )
{
if ( strlen( name ) >= MAX_QPATH ) {
ri.Printf( PRINT_WARNING, "RE_RegisterShader: name exceeds MAX_QPATH\n" );
ri.Printf( PRINT_WARNING, "WARNING: shader name is too long: '%s'\n", name );
return 0;
}
@ -2822,7 +2907,7 @@ qhandle_t RE_RegisterShaderNoMip( const char* name )
const shader_t* R_GetShaderByHandle( qhandle_t hShader )
{
if ((hShader < 0) || (hShader >= tr.numShaders)) {
ri.Printf( PRINT_WARNING, "R_GetShaderByHandle: out of range hShader '%d'\n", hShader );
ri.Printf( PRINT_WARNING, "R_GetShaderByHandle: shader handle out of range: %d\n", hShader );
return tr.defaultShader;
}
return tr.shaders[hShader];

View file

@ -329,7 +329,7 @@ char *Sys_GetClipboardData( void )
data = (char*)Z_Malloc( GlobalSize( hClipboardData ) + 1 );
Q_strncpyz( data, cliptext, GlobalSize( hClipboardData ) );
GlobalUnlock( hClipboardData );
strtok( data, "\n\r\b" );
//strtok( data, "\n\r\b" ); // keep all the lines...
}
}