diff --git a/code/client/cl_imgui.cpp b/code/client/cl_imgui.cpp index a0120c8..b2a8409 100644 --- a/code/client/cl_imgui.cpp +++ b/code/client/cl_imgui.cpp @@ -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; diff --git a/code/renderer/grp_world.cpp b/code/renderer/grp_world.cpp index 544e2d2..9358b95 100644 --- a/code/renderer/grp_world.cpp +++ b/code/renderer/grp_world.cpp @@ -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; diff --git a/code/renderer/tr_gui.cpp b/code/renderer/tr_gui.cpp index b72717d..bbd023c 100644 --- a/code/renderer/tr_gui.cpp +++ b/code/renderer/tr_gui.cpp @@ -27,6 +27,7 @@ along with Challenge Quake 3. If not, see . #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(); } diff --git a/code/renderer/tr_local.h b/code/renderer/tr_local.h index ac75885..dbc22f2 100644 --- a/code/renderer/tr_local.h +++ b/code/renderer/tr_local.h @@ -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 ); /* ==================================================================== diff --git a/code/renderer/tr_shader.cpp b/code/renderer/tr_shader.cpp index ca9f13e..00763ab 100644 --- a/code/renderer/tr_shader.cpp +++ b/code/renderer/tr_shader.cpp @@ -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(); - *newShader = shader; + shader_t* newShader; + if ( sh != NULL ) { + newShader = sh; + *newShader = shader; + } else { + newShader = RI_New(); + *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]; diff --git a/code/win32/win_main.cpp b/code/win32/win_main.cpp index 54c46a8..e88a168 100644 --- a/code/win32/win_main.cpp +++ b/code/win32/win_main.cpp @@ -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... } }