From 3a31008aa8abe8a546f7614af7f94bf95cf0a36e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Lu=C3=ADs=20Vaz=20Silva?= Date: Mon, 6 Jan 2025 13:30:42 -0300 Subject: [PATCH] implement global shaders --- .../rendering/hwrenderer/data/hw_cvars.cpp | 2 + .../rendering/hwrenderer/data/hw_cvars.h | 2 + .../rendering/vulkan/shaders/vk_shader.cpp | 30 +- src/common/textures/gametexture.h | 9 + src/common/textures/hw_material.cpp | 24 +- src/common/textures/textures.h | 7 +- src/d_main.cpp | 5 + src/r_data/gldefs.cpp | 681 ++++++++++++------ 8 files changed, 521 insertions(+), 239 deletions(-) diff --git a/src/common/rendering/hwrenderer/data/hw_cvars.cpp b/src/common/rendering/hwrenderer/data/hw_cvars.cpp index cd8b72d40..db0da86ac 100644 --- a/src/common/rendering/hwrenderer/data/hw_cvars.cpp +++ b/src/common/rendering/hwrenderer/data/hw_cvars.cpp @@ -158,3 +158,5 @@ CUSTOM_CVAR(Int, gl_shadowmap_quality, 512, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) break; } } + +CVAR(Bool, gl_strict_gldefs, true, CVAR_GLOBALCONFIG | CVAR_ARCHIVE) \ No newline at end of file diff --git a/src/common/rendering/hwrenderer/data/hw_cvars.h b/src/common/rendering/hwrenderer/data/hw_cvars.h index aeeb33c9d..54688884d 100644 --- a/src/common/rendering/hwrenderer/data/hw_cvars.h +++ b/src/common/rendering/hwrenderer/data/hw_cvars.h @@ -58,3 +58,5 @@ EXTERN_CVAR(Bool, gl_notexturefill) EXTERN_CVAR(Bool, r_radarclipper) EXTERN_CVAR(Bool, r_dithertransparency) + +EXTERN_CVAR(Bool, gl_strict_gldefs) \ No newline at end of file diff --git a/src/common/rendering/vulkan/shaders/vk_shader.cpp b/src/common/rendering/vulkan/shaders/vk_shader.cpp index 23197f218..b51ff1ae0 100644 --- a/src/common/rendering/vulkan/shaders/vk_shader.cpp +++ b/src/common/rendering/vulkan/shaders/vk_shader.cpp @@ -131,21 +131,21 @@ VkShaderProgram* VkShaderManager::Get(const VkShaderKey& key) // Note: the MaterialShaderIndex enum needs to be updated whenever this array is modified. static const FDefaultShader defaultshaders[] = { - {"Default", "shaders/scene/material_default.glsl", "shaders/scene/mateffect_default.glsl", "shaders/scene/lightmodel_shared.glsl", "shaders/scene/lightmodel_normal.glsl", ""}, - {"Warp 1", "shaders/scene/material_default.glsl", "shaders/scene/mateffect_warp1.glsl", "shaders/scene/lightmodel_shared.glsl", "shaders/scene/lightmodel_normal.glsl", ""}, - {"Warp 2", "shaders/scene/material_default.glsl", "shaders/scene/mateffect_warp2.glsl", "shaders/scene/lightmodel_shared.glsl", "shaders/scene/lightmodel_normal.glsl", ""}, - {"Specular", "shaders/scene/material_spec.glsl", "shaders/scene/mateffect_default.glsl", "shaders/scene/lightmodel_shared.glsl", "shaders/scene/lightmodel_specular.glsl", "#define SPECULAR\n#define NORMALMAP\n"}, - {"PBR", "shaders/scene/material_pbr.glsl", "shaders/scene/mateffect_default.glsl", "shaders/scene/lightmodel_shared.glsl", "shaders/scene/lightmodel_pbr.glsl", "#define PBR\n#define NORMALMAP\n"}, - {"Paletted", "shaders/scene/material_paletted.glsl", "shaders/scene/mateffect_default.glsl", nullptr, "shaders/scene/lightmodel_nolights.glsl", "#define PALETTE_EMULATION\n"}, - {"No Texture", "shaders/scene/material_notexture.glsl", "shaders/scene/mateffect_default.glsl", "shaders/scene/lightmodel_shared.glsl", "shaders/scene/lightmodel_normal.glsl", "#define NO_LAYERS\n"}, - {"Basic Fuzz", "shaders/scene/material_fuzz_standard.glsl", "shaders/scene/mateffect_default.glsl", "shaders/scene/lightmodel_shared.glsl", "shaders/scene/lightmodel_normal.glsl", ""}, - {"Smooth Fuzz", "shaders/scene/material_fuzz_smooth.glsl", "shaders/scene/mateffect_default.glsl", "shaders/scene/lightmodel_shared.glsl", "shaders/scene/lightmodel_normal.glsl", ""}, - {"Swirly Fuzz", "shaders/scene/material_fuzz_swirly.glsl", "shaders/scene/mateffect_default.glsl", "shaders/scene/lightmodel_shared.glsl", "shaders/scene/lightmodel_normal.glsl", ""}, - {"Translucent Fuzz", "shaders/scene/material_fuzz_smoothtranslucent.glsl", "shaders/scene/mateffect_default.glsl", "shaders/scene/lightmodel_shared.glsl", "shaders/scene/lightmodel_normal.glsl", ""}, - {"Jagged Fuzz", "shaders/scene/material_fuzz_jagged.glsl", "shaders/scene/mateffect_default.glsl", "shaders/scene/lightmodel_shared.glsl", "shaders/scene/lightmodel_normal.glsl", ""}, - {"Noise Fuzz", "shaders/scene/material_fuzz_noise.glsl", "shaders/scene/mateffect_default.glsl", "shaders/scene/lightmodel_shared.glsl", "shaders/scene/lightmodel_normal.glsl", ""}, - {"Smooth Noise Fuzz", "shaders/scene/material_fuzz_smoothnoise.glsl", "shaders/scene/mateffect_default.glsl", "shaders/scene/lightmodel_shared.glsl", "shaders/scene/lightmodel_normal.glsl", ""}, - {"Software Fuzz", "shaders/scene/material_fuzz_software.glsl", "shaders/scene/mateffect_default.glsl", "shaders/scene/lightmodel_shared.glsl", "shaders/scene/lightmodel_normal.glsl", ""}, + {"Default", "shaders/scene/material_default.glsl", "shaders/scene/mateffect_default.glsl", "shaders/scene/lightmodel_shared.glsl", "shaders/scene/lightmodel_normal.glsl", "#define SHADERTYPE_DEFAULT\n"}, + {"Warp 1", "shaders/scene/material_default.glsl", "shaders/scene/mateffect_warp1.glsl", "shaders/scene/lightmodel_shared.glsl", "shaders/scene/lightmodel_normal.glsl", "#define SHADERTYPE_WARP1\n"}, + {"Warp 2", "shaders/scene/material_default.glsl", "shaders/scene/mateffect_warp2.glsl", "shaders/scene/lightmodel_shared.glsl", "shaders/scene/lightmodel_normal.glsl", "#define SHADERTYPE_WARP2\n"}, + {"Specular", "shaders/scene/material_spec.glsl", "shaders/scene/mateffect_default.glsl", "shaders/scene/lightmodel_shared.glsl", "shaders/scene/lightmodel_specular.glsl", "#define SHADERTYPE_SPECULAR\n#define SPECULAR\n#define NORMALMAP\n"}, + {"PBR", "shaders/scene/material_pbr.glsl", "shaders/scene/mateffect_default.glsl", "shaders/scene/lightmodel_shared.glsl", "shaders/scene/lightmodel_pbr.glsl", "#define SHADERTYPE_PBR\n#define PBR\n#define NORMALMAP\n"}, + {"Paletted", "shaders/scene/material_paletted.glsl", "shaders/scene/mateffect_default.glsl", nullptr, "shaders/scene/lightmodel_nolights.glsl", "#define SHADERTYPE_PALETTE\n#define PALETTE_EMULATION\n"}, + {"No Texture", "shaders/scene/material_notexture.glsl", "shaders/scene/mateffect_default.glsl", "shaders/scene/lightmodel_shared.glsl", "shaders/scene/lightmodel_normal.glsl", "#define SHADERTYPE_NOTEXTURE\n#define NO_LAYERS\n"}, + {"Basic Fuzz", "shaders/scene/material_fuzz_standard.glsl", "shaders/scene/mateffect_default.glsl", "shaders/scene/lightmodel_shared.glsl", "shaders/scene/lightmodel_normal.glsl", "#define SHADERTYPE_FUZZ\n#define SHADERTYPE_FUZZ_BASIC\n"}, + {"Smooth Fuzz", "shaders/scene/material_fuzz_smooth.glsl", "shaders/scene/mateffect_default.glsl", "shaders/scene/lightmodel_shared.glsl", "shaders/scene/lightmodel_normal.glsl", "#define SHADERTYPE_FUZZ\n#define SHADERTYPE_FUZZ_SMOOTH\n"}, + {"Swirly Fuzz", "shaders/scene/material_fuzz_swirly.glsl", "shaders/scene/mateffect_default.glsl", "shaders/scene/lightmodel_shared.glsl", "shaders/scene/lightmodel_normal.glsl", "#define SHADERTYPE_FUZZ\n#define SHADERTYPE_FUZZ_SWIRLY\n"}, + {"Translucent Fuzz", "shaders/scene/material_fuzz_smoothtranslucent.glsl", "shaders/scene/mateffect_default.glsl", "shaders/scene/lightmodel_shared.glsl", "shaders/scene/lightmodel_normal.glsl", "#define SHADERTYPE_FUZZ\n#define SHADERTYPE_FUZZ_TRANSLUCENT\n"}, + {"Jagged Fuzz", "shaders/scene/material_fuzz_jagged.glsl", "shaders/scene/mateffect_default.glsl", "shaders/scene/lightmodel_shared.glsl", "shaders/scene/lightmodel_normal.glsl", "#define SHADERTYPE_FUZZ\n#define SHADERTYPE_FUZZ_JAGGED\n"}, + {"Noise Fuzz", "shaders/scene/material_fuzz_noise.glsl", "shaders/scene/mateffect_default.glsl", "shaders/scene/lightmodel_shared.glsl", "shaders/scene/lightmodel_normal.glsl", "#define SHADERTYPE_FUZZ\n#define SHADERTYPE_FUZZ_NOISE\n"}, + {"Smooth Noise Fuzz", "shaders/scene/material_fuzz_smoothnoise.glsl", "shaders/scene/mateffect_default.glsl", "shaders/scene/lightmodel_shared.glsl", "shaders/scene/lightmodel_normal.glsl", "#define SHADERTYPE_FUZZ\n#define SHADERTYPE_FUZZ_SMOOTHNOISE\n"}, + {"Software Fuzz", "shaders/scene/material_fuzz_software.glsl", "shaders/scene/mateffect_default.glsl", "shaders/scene/lightmodel_shared.glsl", "shaders/scene/lightmodel_normal.glsl", "#define SHADERTYPE_FUZZ\n#define SHADERTYPE_FUZZ_SOFTWARE\n"}, {nullptr,nullptr,nullptr,nullptr} }; diff --git a/src/common/textures/gametexture.h b/src/common/textures/gametexture.h index 2946ce61d..3f929fca1 100644 --- a/src/common/textures/gametexture.h +++ b/src/common/textures/gametexture.h @@ -89,6 +89,15 @@ struct FMaterialLayers MaterialLayerSampling CustomShaderTextureSampling[MAX_CUSTOM_HW_SHADER_TEXTURES]; }; +struct GlobalShaderDesc +{ + int shaderindex = -1; + RefCountedPtr CustomShaderTextures[MAX_CUSTOM_HW_SHADER_TEXTURES]; + MaterialLayerSampling CustomShaderTextureSampling[MAX_CUSTOM_HW_SHADER_TEXTURES]; +}; + +extern GlobalShaderDesc globalshaders[NUM_BUILTIN_SHADERS]; + // Refactoring helper to allow piece by piece adjustment of the API class FGameTexture { diff --git a/src/common/textures/hw_material.cpp b/src/common/textures/hw_material.cpp index bbfe61e16..5b9df5844 100644 --- a/src/common/textures/hw_material.cpp +++ b/src/common/textures/hw_material.cpp @@ -128,26 +128,40 @@ FMaterial::FMaterial(FGameTexture * tx, int scaleflags) } auto index = tx->GetShaderIndex(); + if (gl_customshader) { - if (index >= FIRST_USER_SHADER) + if (index >= FIRST_USER_SHADER || (mShaderIndex < FIRST_USER_SHADER && globalshaders[mShaderIndex].shaderindex >= FIRST_USER_SHADER)) { - const UserShaderDesc& usershader = usershaders[index - FIRST_USER_SHADER]; - if (usershader.shaderType == mShaderIndex) // Only apply user shader if it matches the expected material + + if (index >= FIRST_USER_SHADER && usershaders[index - FIRST_USER_SHADER].shaderType == mShaderIndex) // Only apply user shader if it matches the expected material { if (tx->Layers) { - size_t index = 0; + size_t i = 0; for (auto& texture : tx->Layers->CustomShaderTextures) { if (texture != nullptr) { - mTextureLayers.Push({ texture.get(), 0, -1, tx->Layers->CustomShaderTextureSampling[index++]}); // scalability should be user-definable. + mTextureLayers.Push({ texture.get(), 0, -1, tx->Layers->CustomShaderTextureSampling[i++]}); // scalability should be user-definable. } } } mShaderIndex = index; } + else if(mShaderIndex < FIRST_USER_SHADER && globalshaders[mShaderIndex].shaderindex >= FIRST_USER_SHADER) + { + size_t i = 0; + for (auto& texture : globalshaders[mShaderIndex].CustomShaderTextures) + { + if (texture != nullptr) + { + mTextureLayers.Push({ texture.get(), 0, -1, globalshaders[mShaderIndex].CustomShaderTextureSampling[i++]}); // scalability should be user-definable. + } + } + + mShaderIndex = globalshaders[mShaderIndex].shaderindex; + } } } } diff --git a/src/common/textures/textures.h b/src/common/textures/textures.h index 34dbdd504..5902cb476 100644 --- a/src/common/textures/textures.h +++ b/src/common/textures/textures.h @@ -82,7 +82,8 @@ enum MaterialShaderIndex SHADER_NoiseFuzz, SHADER_SmoothNoiseFuzz, SHADER_SoftwareFuzz, - FIRST_USER_SHADER + NUM_BUILTIN_SHADERS, + FIRST_USER_SHADER = NUM_BUILTIN_SHADERS }; enum texflags @@ -98,9 +99,7 @@ enum texflags enum { - SFlag_Brightmap = 1, - SFlag_Detailmap = 2, - SFlag_Glowmap = 4, + SFlag_Global = 1, }; struct UserShaderDesc diff --git a/src/d_main.cpp b/src/d_main.cpp index 63ed53fba..6cf86d3bd 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -3972,6 +3972,11 @@ void D_Cleanup() { *(afunc->VMPointer) = NULL; }); + + for(auto &gshader : globalshaders) + { + gshader = {}; + } GC::DelSoftRootHead(); diff --git a/src/r_data/gldefs.cpp b/src/r_data/gldefs.cpp index 3169aa5d6..d1c163c6d 100644 --- a/src/r_data/gldefs.cpp +++ b/src/r_data/gldefs.cpp @@ -59,6 +59,8 @@ void ParseColorization(FScanner& sc); extern TDeletingArray LightDefaults; extern int AttenuationIsSet; +GlobalShaderDesc globalshaders[NUM_BUILTIN_SHADERS]; + struct ExtraUniformCVARData { FString Shader; @@ -1315,6 +1317,10 @@ class GLDefsParser bool iwad = false; bool no_mipmap = false; + bool is_globalshader = false; + TArray globaltargets; + FString str_globaltargets; + UserShaderDesc usershader; TArray texNameList; TArray texNameIndex; @@ -1324,263 +1330,508 @@ class GLDefsParser FGameTexture* textures[6] = {}; const char *keywords[7] = { "brightmap", "normal", "specular", "metallic", "roughness", "ao", nullptr }; const char *notFound[6] = { "Brightmap", "Normalmap", "Specular texture", "Metallic texture", "Roughness texture", "Ambient occlusion texture" }; + + FGameTexture* tex = nullptr; sc.MustGetString(); - if (sc.Compare("texture")) type = ETextureType::Wall; - else if (sc.Compare("flat")) type = ETextureType::Flat; - else if (sc.Compare("sprite")) type = ETextureType::Sprite; - else sc.UnGet(); - - sc.MustGetString(); - FTextureID no = TexMan.CheckForTexture(sc.String, type, FTextureManager::TEXMAN_TryAny | FTextureManager::TEXMAN_Overridable); - auto tex = TexMan.GetGameTexture(no); - - if (tex == nullptr) + if(sc.Compare("globalshader")) { - sc.ScriptMessage("Material definition refers nonexistent texture '%s'\n", sc.String); + is_globalshader = true; + usershader.shaderFlags |= SFlag_Global; // make sure global usershader objects aren't reused for material shaders + + sc.MustGetString(); + + if (sc.Compare("all")) + { + str_globaltargets = "all"; + globaltargets.Push(SHADER_Default); + globaltargets.Push(SHADER_Warp1); + globaltargets.Push(SHADER_Warp2); + globaltargets.Push(SHADER_Specular); + globaltargets.Push(SHADER_PBR); + globaltargets.Push(SHADER_Paletted); + globaltargets.Push(SHADER_NoTexture); + globaltargets.Push(SHADER_BasicFuzz); + globaltargets.Push(SHADER_SmoothFuzz); + globaltargets.Push(SHADER_SwirlyFuzz); + globaltargets.Push(SHADER_TranslucentFuzz); + globaltargets.Push(SHADER_JaggedFuzz); + globaltargets.Push(SHADER_NoiseFuzz); + globaltargets.Push(SHADER_SmoothNoiseFuzz); + globaltargets.Push(SHADER_SoftwareFuzz); + } + else if(sc.Compare("default")) + { + str_globaltargets = "default"; + globaltargets.Push(SHADER_Default); + } + else if(sc.Compare("defaultwarp")) + { + str_globaltargets = "defaultwarp"; + globaltargets.Push(SHADER_Default); + globaltargets.Push(SHADER_Warp1); + globaltargets.Push(SHADER_Warp2); + } + else if(sc.Compare("warp")) + { + str_globaltargets = "warp"; + globaltargets.Push(SHADER_Warp1); + globaltargets.Push(SHADER_Warp2); + } + else if(sc.Compare("specular")) + { + str_globaltargets = "specular"; + globaltargets.Push(SHADER_Specular); + } + else if(sc.Compare("pbr")) + { + str_globaltargets = "pbr"; + globaltargets.Push(SHADER_PBR); + } + else if(sc.Compare("paletted")) + { + str_globaltargets = "paletted"; + globaltargets.Push(SHADER_Paletted); + } + else if(sc.Compare("notexture")) + { + str_globaltargets = "notexture"; + globaltargets.Push(SHADER_NoTexture); + } + else if(sc.Compare("nonfuzz")) + { + str_globaltargets = "nonfuzz"; + globaltargets.Push(SHADER_Default); + globaltargets.Push(SHADER_Warp1); + globaltargets.Push(SHADER_Warp2); + globaltargets.Push(SHADER_Specular); + globaltargets.Push(SHADER_PBR); + globaltargets.Push(SHADER_Paletted); + } + else if(sc.Compare("fuzz")) + { + str_globaltargets = "fuzz"; + globaltargets.Push(SHADER_BasicFuzz); + globaltargets.Push(SHADER_SmoothFuzz); + globaltargets.Push(SHADER_SwirlyFuzz); + globaltargets.Push(SHADER_TranslucentFuzz); + globaltargets.Push(SHADER_JaggedFuzz); + globaltargets.Push(SHADER_NoiseFuzz); + globaltargets.Push(SHADER_SmoothNoiseFuzz); + globaltargets.Push(SHADER_SoftwareFuzz); + } + else + { + sc.ScriptMessage("Invalid globalshader target\n", sc.String); + } } - else tex->AddAutoMaterials(); // We need these before setting up the texture. + else + { + if (sc.Compare("texture")) type = ETextureType::Wall; + else if (sc.Compare("flat")) type = ETextureType::Flat; + else if (sc.Compare("sprite")) type = ETextureType::Sprite; + else sc.UnGet(); + + sc.MustGetString(); + FTextureID no = TexMan.CheckForTexture(sc.String, type, FTextureManager::TEXMAN_TryAny | FTextureManager::TEXMAN_Overridable); + tex = TexMan.GetGameTexture(no); + + if (tex == nullptr) + { + sc.ScriptMessage("Material definition refers nonexistent texture '%s'\n", sc.String); + } + else tex->AddAutoMaterials(); // We need these before setting up the texture. + } + + bool do_strict = gl_strict_gldefs || is_globalshader; sc.MustGetToken('{'); while (!sc.CheckToken('}')) { + bool isProperty = false; + sc.MustGetString(); - if (sc.Compare("disablefullbright")) + + if(!is_globalshader) { - // This can also be used without a brightness map to disable - // fullbright in rotations that only use brightness maps on - // other angles. - disable_fullbright = true; - disable_fullbright_specified = true; - } - else if (sc.Compare("thiswad")) - { - // only affects textures defined in the WAD containing the definition file. - thiswad = true; - } - else if (sc.Compare ("iwad")) - { - // only affects textures defined in the IWAD. - iwad = true; - } - else if (sc.Compare("nomipmap")) - { - no_mipmap = true; - } - else if (sc.Compare("glossiness")) - { - sc.MustGetFloat(); - mlay.Glossiness = (float)sc.Float; - } - else if (sc.Compare("specularlevel")) - { - sc.MustGetFloat(); - mlay.SpecularLevel = (float)sc.Float; - } - else if (sc.Compare("depthfadethreshold")) - { - sc.MustGetFloat(); - tex->DepthFadeThreshold = (float)sc.Float; - } - else if (sc.Compare("speed")) - { - sc.MustGetFloat(); - speed = float(sc.Float); - } - else if (sc.Compare("shader")) - { - sc.MustGetString(); - usershader.shader = sc.String; - } - else if (sc.Compare("disablealphatest")) - { - tex->SetTranslucent(true); - if (usershader.shader.IsNotEmpty()) - usershader.disablealphatest = true; - } - else if (sc.Compare("texture")) - { - sc.MustGetString(); - FString textureName = sc.String; - for (FString &texName : texNameList) + if (sc.Compare("disablefullbright")) { - if (!texName.Compare(textureName)) - { - sc.ScriptError("Trying to redefine custom hardware shader texture '%s' in texture '%s'\n", textureName.GetChars(), tex ? tex->GetName().GetChars() : "(null)"); - } + isProperty = true; + // This can also be used without a brightness map to disable + // fullbright in rotations that only use brightness maps on + // other angles. + disable_fullbright = true; + disable_fullbright_specified = true; } - sc.MustGetString(); - if (tex) + else if (sc.Compare("thiswad")) { - bool okay = false; - size_t texIndex = 0; - for (size_t i = 0; i < countof(mlay.CustomShaderTextures); i++) + isProperty = true; + // only affects textures defined in the WAD containing the definition file. + thiswad = true; + } + else if (sc.Compare ("iwad")) + { + // only affects textures defined in the IWAD. + iwad = true; + isProperty = true; + } + else if (sc.Compare("nomipmap")) + { + isProperty = true; + no_mipmap = true; + } + else if (sc.Compare("glossiness")) + { + isProperty = true; + sc.MustGetFloat(); + mlay.Glossiness = (float)sc.Float; + } + else if (sc.Compare("specularlevel")) + { + isProperty = true; + sc.MustGetFloat(); + mlay.SpecularLevel = (float)sc.Float; + } + else if (sc.Compare("depthfadethreshold")) + { + isProperty = true; + sc.MustGetFloat(); + tex->DepthFadeThreshold = (float)sc.Float; + } + else if (sc.Compare("speed")) + { + isProperty = true; + sc.MustGetFloat(); + speed = float(sc.Float); + } + else if (sc.Compare("disablealphatest")) + { + isProperty = true; + tex->SetTranslucent(true); + if (usershader.shader.IsNotEmpty()) + usershader.disablealphatest = true; + } + } + if(!isProperty) + { + if (sc.Compare("shader")) + { + isProperty = true; + sc.MustGetString(); + usershader.shader = sc.String; + } + else if (sc.Compare("texture")) + { + isProperty = true; + sc.MustGetString(); + FString textureName = sc.String; + for (FString &texName : texNameList) { - if (!mlay.CustomShaderTextures[i]) + if (!texName.Compare(textureName)) + { + if(is_globalshader) + { + sc.ScriptError("Trying to redefine custom hardware shader texture '%s' in global shader '%s'\n", textureName.GetChars(), str_globaltargets.GetChars()); + } + else + { + sc.ScriptError("Trying to redefine custom hardware shader texture '%s' in texture '%s'\n", textureName.GetChars(), tex ? tex->GetName().GetChars() : "(null)"); + } + } + } + sc.MustGetString(); + if (tex || is_globalshader) + { + bool okay = false; + size_t texIndex = 0; + for (size_t i = 0; i < countof(mlay.CustomShaderTextures); i++) { - mlay.CustomShaderTextureSampling[texIndex] = MaterialLayerSampling::Default; - mlay.CustomShaderTextures[i] = TexMan.FindGameTexture(sc.String, ETextureType::Any, FTextureManager::TEXMAN_TryAny); if (!mlay.CustomShaderTextures[i]) { - sc.ScriptError("Custom hardware shader texture '%s' not found in texture '%s'\n", sc.String, tex->GetName().GetChars()); - } + mlay.CustomShaderTextureSampling[texIndex] = MaterialLayerSampling::Default; + mlay.CustomShaderTextures[i] = TexMan.FindGameTexture(sc.String, ETextureType::Any, FTextureManager::TEXMAN_TryAny); + if (!mlay.CustomShaderTextures[i]) + { + if(is_globalshader) + { + sc.ScriptError("Custom hardware shader texture '%s' not found in global shader '%s'\n", sc.String, str_globaltargets.GetChars()); + } + else + { + sc.ScriptError("Custom hardware shader texture '%s' not found in texture '%s'\n", sc.String, tex->GetName().GetChars()); + } + } - texNameList.Push(textureName); - texNameIndex.Push((int)i); - texIndex = i; - okay = true; + texNameList.Push(textureName); + texNameIndex.Push((int)i); + texIndex = i; + okay = true; + break; + } + } + if (!okay) + { + if(is_globalshader) + { + sc.ScriptError("Error: out of texture units in global shader '%s'\n", str_globaltargets.GetChars()); + } + else + { + sc.ScriptError("Error: out of texture units in texture '%s'", tex->GetName().GetChars()); + } + } + + if (sc.CheckToken('{')) + { + while (!sc.CheckToken('}')) + { + sc.MustGetString(); + if (sc.Compare("filter")) + { + sc.MustGetString(); + if (sc.Compare("nearest")) + { + if(okay) + mlay.CustomShaderTextureSampling[texIndex] = MaterialLayerSampling::NearestMipLinear; + } + else if (sc.Compare("linear")) + { + if (okay) + mlay.CustomShaderTextureSampling[texIndex] = MaterialLayerSampling::LinearMipLinear; + } + else if (sc.Compare("default")) + { + if (okay) + mlay.CustomShaderTextureSampling[texIndex] = MaterialLayerSampling::Default; + } + else + { + if(is_globalshader) + { + sc.ScriptError("Error: unexpected '%s' when reading filter property in global shader '%s'\n", sc.String, str_globaltargets.GetChars()); + } + else + { + sc.ScriptError("Error: unexpected '%s' when reading filter property in texture '%s'\n", sc.String, tex ? tex->GetName().GetChars() : "(null)"); + } + } + } + } + } + } + } + else if (sc.Compare("define")) + { + isProperty = true; + sc.MustGetString(); + FString defineName = sc.String; + FString defineValue = ""; + if (sc.CheckToken('=')) + { + sc.MustGetString(); + defineValue = sc.String; + } + usershader.defines.AppendFormat("#define %s %s\n", defineName.GetChars(), defineValue.GetChars()); + } + else if(!is_globalshader) + { + for (int i = 0; keywords[i] != nullptr; i++) + { + if (sc.Compare (keywords[i])) + { + isProperty = true; + sc.MustGetString(); + if (textures[i]) + Printf("Multiple %s definitions in texture %s\n", keywords[i], tex? tex->GetName().GetChars() : "(null)"); + textures[i] = TexMan.FindGameTexture(sc.String, ETextureType::Any, FTextureManager::TEXMAN_TryAny); + if (!textures[i]) + Printf("%s '%s' not found in texture '%s'\n", notFound[i], sc.String, tex? tex->GetName().GetChars() : "(null)"); break; } } - if (!okay) - { - sc.ScriptError("Error: out of texture units in texture '%s'", tex->GetName().GetChars()); - } - - if (sc.CheckToken('{')) - { - while (!sc.CheckToken('}')) - { - sc.MustGetString(); - if (sc.Compare("filter")) - { - sc.MustGetString(); - if (sc.Compare("nearest")) - { - if(okay) - mlay.CustomShaderTextureSampling[texIndex] = MaterialLayerSampling::NearestMipLinear; - } - else if (sc.Compare("linear")) - { - if (okay) - mlay.CustomShaderTextureSampling[texIndex] = MaterialLayerSampling::LinearMipLinear; - } - else if (sc.Compare("default")) - { - if (okay) - mlay.CustomShaderTextureSampling[texIndex] = MaterialLayerSampling::Default; - } - else - { - sc.ScriptError("Error: unexpected '%s' when reading filter property in texture '%s'\n", sc.String, tex ? tex->GetName().GetChars() : "(null)"); - } - } - } - } } } - else if (sc.Compare("define")) + if(!isProperty && do_strict) { - sc.MustGetString(); - FString defineName = sc.String; - FString defineValue = ""; - if (sc.CheckToken('=')) + if(is_globalshader) { - sc.MustGetString(); - defineValue = sc.String; + sc.ScriptError("Unknown keyword '%s' in global shader '%s'", sc.String, str_globaltargets.GetChars()); } - usershader.defines.AppendFormat("#define %s %s\n", defineName.GetChars(), defineValue.GetChars()); - } - else - { - for (int i = 0; keywords[i] != nullptr; i++) + else { - if (sc.Compare (keywords[i])) - { - sc.MustGetString(); - if (textures[i]) - Printf("Multiple %s definitions in texture %s\n", keywords[i], tex? tex->GetName().GetChars() : "(null)"); - textures[i] = TexMan.FindGameTexture(sc.String, ETextureType::Any, FTextureManager::TEXMAN_TryAny); - if (!textures[i]) - Printf("%s '%s' not found in texture '%s'\n", notFound[i], sc.String, tex? tex->GetName().GetChars() : "(null)"); - break; - } + sc.ScriptError("Unknown keyword '%s' in texture '%s'", sc.String, tex? tex->GetName().GetChars() : "(null)"); } } } - if (!tex) + if (!tex && !is_globalshader) { return; } - if (thiswad || iwad) + if(is_globalshader) { - bool useme = false; - int lumpnum = tex->GetSourceLump(); - - if (lumpnum != -1) + if(fileSystem.GetFileContainer(workingLump) > fileSystem.GetMaxIwadNum()) { - if (iwad && fileSystem.GetFileContainer(lumpnum) <= fileSystem.GetMaxIwadNum()) useme = true; - if (thiswad && fileSystem.GetFileContainer(lumpnum) == fileSystem.GetFileContainer(workingLump)) useme = true; - } - if (!useme) return; - } - - tex->SetNoMipmap(no_mipmap); - - FGameTexture **bindings[6] = - { - &mlay.Brightmap, - &mlay.Normal, - &mlay.Specular, - &mlay.Metallic, - &mlay.Roughness, - &mlay.AmbientOcclusion - }; - for (int i = 0; keywords[i] != nullptr; i++) - { - if (textures[i]) - { - *bindings[i] = textures[i]; - } - } - - if (disable_fullbright_specified) - tex->SetDisableFullbright(disable_fullbright); - - if (usershader.shader.IsNotEmpty()) - { - int firstUserTexture; - if ((mlay.Normal || tex->GetNormalmap()) && (mlay.Specular || tex->GetSpecularmap())) - { - usershader.shaderType = SHADER_Specular; - firstUserTexture = 7; - } - else if ((mlay.Normal || tex->GetNormalmap()) && (mlay.Metallic || tex->GetMetallic()) && (mlay.Roughness || tex->GetRoughness()) && (mlay.AmbientOcclusion || tex->GetAmbientOcclusion())) - { - usershader.shaderType = SHADER_PBR; - firstUserTexture = 9; - } - else - { - usershader.shaderType = SHADER_Default; - firstUserTexture = 5; - } - - for (unsigned int i = 0; i < texNameList.Size(); i++) - { - usershader.defines.AppendFormat("#define %s texture%d\n", texNameList[i].GetChars(), texNameIndex[i] + firstUserTexture); - } - - if (tex->isWarped() != 0) - { - Printf("Cannot combine warping with hardware shader on texture '%s'\n", tex->GetName().GetChars()); + sc.ScriptError("globalshader only supported on iwad"); return; } - tex->SetShaderSpeed(speed); - for (unsigned i = 0; i < usershaders.Size(); i++) + else for(int target : globaltargets) { - if (!usershaders[i].shader.CompareNoCase(usershader.shader) && - usershaders[i].shaderType == usershader.shaderType && - !usershaders[i].defines.Compare(usershader.defines)) + if(globalshaders[target].shaderindex >= 0) { - SetShaderIndex(tex, i + FIRST_USER_SHADER); - tex->SetShaderLayers(mlay); + sc.ScriptError("globalshader already exists for '%s'", str_globaltargets.GetChars()); return; } } - SetShaderIndex(tex, usershaders.Push(usershader) + FIRST_USER_SHADER); + + if (usershader.shader.IsNotEmpty()) + { + int lump = fileSystem.CheckNumForFullName(usershader.shader.GetChars(), 0); + if (lump == -1) + { + sc.ScriptError("inexistent shader lump '%s' in globalshader '%s'", usershader.shader.GetChars(), str_globaltargets.GetChars()); + return; + } + + for(int target : globaltargets) + { + FString baseDefines = usershader.defines; + + int firstUserTexture; + + if (target == SHADER_Specular) + { + firstUserTexture = 7; + } + else if (target == SHADER_PBR) + { + firstUserTexture = 9; + } + else + { + firstUserTexture = 5; + } + + for (unsigned int i = 0; i < texNameList.Size(); i++) + { + usershader.defines.AppendFormat("#define %s texture%d\n", texNameList[i].GetChars(), texNameIndex[i] + firstUserTexture); + } + + usershader.shaderType = MaterialShaderIndex(target); + + globalshaders[target].shaderindex = usershaders.Push(usershader) + FIRST_USER_SHADER; + + for (int i = 0; i < MAX_CUSTOM_HW_SHADER_TEXTURES; i++) + { + if (mlay.CustomShaderTextures[i]) + { + globalshaders[target].CustomShaderTextureSampling[i] = mlay.CustomShaderTextureSampling[i]; + globalshaders[target].CustomShaderTextures[i] = mlay.CustomShaderTextures[i]->GetTexture(); + } + } + + usershader.defines = baseDefines; + } + } + else + { + sc.ScriptError("shader lump not specified for global shader '%s'", usershader.shader.GetChars(), str_globaltargets.GetChars()); + } + } + else + { + if (thiswad || iwad) + { + bool useme = false; + int lumpnum = tex->GetSourceLump(); + + if (lumpnum != -1) + { + if (iwad && fileSystem.GetFileContainer(lumpnum) <= fileSystem.GetMaxIwadNum()) useme = true; + if (thiswad && fileSystem.GetFileContainer(lumpnum) == fileSystem.GetFileContainer(workingLump)) useme = true; + } + if (!useme) return; + } + + tex->SetNoMipmap(no_mipmap); + + FGameTexture **bindings[6] = + { + &mlay.Brightmap, + &mlay.Normal, + &mlay.Specular, + &mlay.Metallic, + &mlay.Roughness, + &mlay.AmbientOcclusion + }; + for (int i = 0; keywords[i] != nullptr; i++) + { + if (textures[i]) + { + *bindings[i] = textures[i]; + } + } + + if (disable_fullbright_specified) + tex->SetDisableFullbright(disable_fullbright); + + if (usershader.shader.IsNotEmpty()) + { + if(do_strict) + { + int lump = fileSystem.CheckNumForFullName(usershader.shader.GetChars(), 0); + if (lump == -1) + { + sc.ScriptError("inexistent shader lump '%s' in globalshader '%s'", usershader.shader.GetChars(), str_globaltargets.GetChars()); + return; + } + } + + int firstUserTexture; + if ((mlay.Normal || tex->GetNormalmap()) && (mlay.Specular || tex->GetSpecularmap())) + { + usershader.shaderType = SHADER_Specular; + firstUserTexture = 7; + } + else if ((mlay.Normal || tex->GetNormalmap()) && (mlay.Metallic || tex->GetMetallic()) && (mlay.Roughness || tex->GetRoughness()) && (mlay.AmbientOcclusion || tex->GetAmbientOcclusion())) + { + usershader.shaderType = SHADER_PBR; + firstUserTexture = 9; + } + else + { + usershader.shaderType = SHADER_Default; + firstUserTexture = 5; + } + + for (unsigned int i = 0; i < texNameList.Size(); i++) + { + usershader.defines.AppendFormat("#define %s texture%d\n", texNameList[i].GetChars(), texNameIndex[i] + firstUserTexture); + } + + if (tex->isWarped() != 0) + { + Printf("Cannot combine warping with hardware shader on texture '%s'\n", tex->GetName().GetChars()); + return; + } + tex->SetShaderSpeed(speed); + + for (unsigned i = 0; i < usershaders.Size(); i++) + { + if (!usershaders[i].shader.CompareNoCase(usershader.shader) && + usershaders[i].shaderType == usershader.shaderType && + usershaders[i].shaderFlags == usershader.shaderFlags && + !usershaders[i].defines.Compare(usershader.defines)) + { + SetShaderIndex(tex, i + FIRST_USER_SHADER); + tex->SetShaderLayers(mlay); + return; + } + } + + SetShaderIndex(tex, usershaders.Push(usershader) + FIRST_USER_SHADER); + } + tex->SetShaderLayers(mlay); } - tex->SetShaderLayers(mlay); } @@ -1888,7 +2139,6 @@ class GLDefsParser if (sc.Compare(typeName[i])) { desc.shaderType = typeIndex[i]; - if (usesBrightmap[i]) desc.shaderFlags |= SFlag_Brightmap; found = true; break; } @@ -1992,6 +2242,7 @@ class GLDefsParser { if (!usershaders[i].shader.CompareNoCase(desc.shader) && usershaders[i].shaderType == desc.shaderType && + usershaders[i].shaderFlags == desc.shaderFlags && !usershaders[i].defines.Compare(desc.defines)) { SetShaderIndex(tex, i + FIRST_USER_SHADER);