diff --git a/src/gl/renderer/gl_renderer.cpp b/src/gl/renderer/gl_renderer.cpp index d6f3d3c9c..13a18cce7 100644 --- a/src/gl/renderer/gl_renderer.cpp +++ b/src/gl/renderer/gl_renderer.cpp @@ -260,6 +260,26 @@ sector_t *FGLRenderer::RenderView(player_t* player) return retsec; } +//=========================================================================== +// +// +// +//=========================================================================== + +void FGLRenderer::BindToFrameBuffer(FMaterial *mat) +{ + auto BaseLayer = static_cast(mat->GetLayer(0)); + + if (BaseLayer == nullptr) + { + // must create the hardware texture first + BaseLayer->BindOrCreate(mat->sourcetex, 0, 0, 0, 0); + FHardwareTexture::Unbind(0); + gl_RenderState.ClearLastMaterial(); + } + BaseLayer->BindToFrameBuffer(mat->GetWidth(), mat->GetHeight()); +} + //=========================================================================== // // Camera texture rendering @@ -274,7 +294,7 @@ void FGLRenderer::RenderTextureView(FCanvasTexture *tex, AActor *Viewpoint, doub int height = gltex->TextureHeight(); StartOffscreen(); - gltex->BindToFrameBuffer(); + BindToFrameBuffer(gltex); IntRect bounds; bounds.left = bounds.top = 0; diff --git a/src/gl/renderer/gl_renderer.h b/src/gl/renderer/gl_renderer.h index 6bbd5cc46..7551d4428 100644 --- a/src/gl/renderer/gl_renderer.h +++ b/src/gl/renderer/gl_renderer.h @@ -116,9 +116,7 @@ public: bool StartOffscreen(); void EndOffscreen(); - void FillSimplePoly(FTexture *texture, FVector2 *points, int npoints, - double originx, double originy, double scalex, double scaley, - DAngle rotation, const FColormap &colormap, PalEntry flatcolor, int lightlevel, int bottomclip); + void BindToFrameBuffer(FMaterial *mat); }; #include "hwrenderer/scene/hw_fakeflat.h" diff --git a/src/gl/renderer/gl_renderstate.cpp b/src/gl/renderer/gl_renderstate.cpp index 258fc9a05..d9a66a0c6 100644 --- a/src/gl/renderer/gl_renderstate.cpp +++ b/src/gl/renderer/gl_renderstate.cpp @@ -36,6 +36,7 @@ #include "gl/renderer/gl_renderer.h" #include "gl/dynlights//gl_lightbuffer.h" #include "gl/renderer/gl_renderbuffers.h" +#include "gl/textures/gl_hwtexture.h" void gl_SetTextureMode(int type); @@ -288,3 +289,62 @@ void FRenderState::ApplyLightIndex(int index) } activeShader->muLightIndex.Set(index); } + +//=========================================================================== +// +// Binds a texture to the renderer +// +//=========================================================================== + +void FRenderState::SetMaterial(FMaterial *mat, int clampmode, int translation, int overrideshader, bool alphatexture) +{ + if (mat->tex->bHasCanvas) + { + mTempTM = TM_OPAQUE; + } + else + { + mTempTM = TM_MODULATE; + } + mEffectState = overrideshader >= 0 ? overrideshader : mat->mShaderIndex; + mShaderTimer = mat->tex->shaderspeed; + SetSpecular(mat->tex->Glossiness, mat->tex->SpecularLevel); + + // avoid rebinding the same texture multiple times. + if (mat == lastMaterial && lastClamp == clampmode && translation == lastTranslation) return; + lastMaterial = mat; + lastClamp = clampmode; + lastTranslation = translation; + + int usebright = false; + int maxbound = 0; + auto tex = mat->tex; + + if (tex->UseType == ETextureType::SWCanvas) clampmode = CLAMP_NOFILTER; + if (tex->bHasCanvas) clampmode = CLAMP_CAMTEX; + else if (tex->bWarped && clampmode <= CLAMP_XY) clampmode = CLAMP_NONE; + + // Textures that are already scaled in the texture lump will not get replaced by hires textures. + int flags = mat->isExpanded() ? CTF_Expand : (gl_texture_usehires && tex->Scale.X == 1 && tex->Scale.Y == 1 && clampmode <= CLAMP_XY) ? CTF_CheckHires : 0; + int numLayers = mat->GetLayers(); + auto base = static_cast(mat->GetLayer(0)); + + if (base->BindOrCreate(tex, 0, clampmode, translation, flags)) + { + for (int i = 1; i(mat->GetLayer(i, &layer)); + systex->BindOrCreate(layer, i, clampmode, 0, mat->isExpanded() ? CTF_Expand : 0); + maxbound = i; + } + } + // unbind everything from the last texture that's still active + for (int i = maxbound + 1; i <= maxBoundMaterial; i++) + { + FHardwareTexture::Unbind(i); + maxBoundMaterial = maxbound; + } +} + + diff --git a/src/gl/renderer/gl_renderstate.h b/src/gl/renderer/gl_renderstate.h index e1dc4bec0..e3a7615b3 100644 --- a/src/gl/renderer/gl_renderstate.h +++ b/src/gl/renderer/gl_renderstate.h @@ -124,6 +124,13 @@ class FRenderState bool ApplyShader(); + // Texture binding state + FMaterial *lastMaterial = nullptr; + int lastClamp = 0; + int lastTranslation = 0; + int maxBoundMaterial = -1; + + public: VSMatrix mModelMatrix; @@ -136,22 +143,13 @@ public: void Reset(); - void SetMaterial(FMaterial *mat, int clampmode, int translation, int overrideshader, bool alphatexture) + void ClearLastMaterial() { - if (mat->tex->bHasCanvas) - { - mTempTM = TM_OPAQUE; - } - else - { - mTempTM = TM_MODULATE; - } - mEffectState = overrideshader >= 0? overrideshader : mat->mShaderIndex; - mShaderTimer = mat->tex->shaderspeed; - SetSpecular(mat->tex->Glossiness, mat->tex->SpecularLevel); - mat->Bind(clampmode, translation); + lastMaterial = nullptr; } + void SetMaterial(FMaterial *mat, int clampmode, int translation, int overrideshader, bool alphatexture); + void Apply(); void ApplyColorMask(); void ApplyLightIndex(int index); diff --git a/src/gl/shaders/gl_shader.cpp b/src/gl/shaders/gl_shader.cpp index a5a7a7249..c9d15103b 100644 --- a/src/gl/shaders/gl_shader.cpp +++ b/src/gl/shaders/gl_shader.cpp @@ -488,6 +488,8 @@ static const FDefaultShader defaultshaders[]= }; TArray usershaders; +TArray usermaterials; +TArray usershaderdefs; struct FEffectShader { @@ -609,7 +611,7 @@ void FShaderCollection::CompileShaders(EPassType passType) FString name = ExtractFileBase(usershaders[i]); FName sfn = name; - FShader *shc = Compile(sfn, usershaders[i], "shaders/glsl/material_normal.fp", "", true, passType); + FShader *shc = Compile(sfn, usershaders[i], usermaterials[i], usershaderdefs[i], true, passType); mMaterialShaders.Push(shc); } diff --git a/src/gl/system/gl_framebuffer.cpp b/src/gl/system/gl_framebuffer.cpp index 6f0171b0c..0cfdc1ad1 100644 --- a/src/gl/system/gl_framebuffer.cpp +++ b/src/gl/system/gl_framebuffer.cpp @@ -73,7 +73,6 @@ OpenGLFrameBuffer::OpenGLFrameBuffer(void *hMonitor, bool fullscreen) : // Make sure all global variables tracking OpenGL context state are reset.. FHardwareTexture::InitGlobalState(); - FMaterial::InitGlobalState(); gl_RenderState.Reset(); GLRenderer = new FGLRenderer(this); @@ -339,6 +338,11 @@ IHardwareTexture *OpenGLFrameBuffer::CreateHardwareTexture(FTexture *tex) return new FHardwareTexture(tex->bNoCompress); } +void OpenGLFrameBuffer::PrecacheMaterial(FMaterial *mat, int translation) +{ + gl_RenderState.SetMaterial(mat, CLAMP_NONE, translation, false, false); +} + FModelRenderer *OpenGLFrameBuffer::CreateModelRenderer(int mli) { return new FGLModelRenderer(nullptr, mli); @@ -355,11 +359,6 @@ IShaderProgram *OpenGLFrameBuffer::CreateShaderProgram() } -void OpenGLFrameBuffer::UnbindTexUnit(int no) -{ - FHardwareTexture::Unbind(no); -} - void OpenGLFrameBuffer::TextureFilterChanged() { if (GLRenderer != NULL && GLRenderer->mSamplerManager != NULL) GLRenderer->mSamplerManager->SetTextureFilterMode(); diff --git a/src/gl/system/gl_framebuffer.h b/src/gl/system/gl_framebuffer.h index d7aa5760a..817ae96b5 100644 --- a/src/gl/system/gl_framebuffer.h +++ b/src/gl/system/gl_framebuffer.h @@ -35,8 +35,8 @@ public: sector_t *RenderView(player_t *player) override; void SetTextureFilterMode() override; IHardwareTexture *CreateHardwareTexture(FTexture *tex) override; + void PrecacheMaterial(FMaterial *mat, int translation) override; FModelRenderer *CreateModelRenderer(int mli) override; - void UnbindTexUnit(int no) override; void TextureFilterChanged() override; void BeginFrame() override; void SetViewportRects(IntRect *bounds) override; diff --git a/src/gl/system/gl_wipe.cpp b/src/gl/system/gl_wipe.cpp index c6f6bab50..ac5b924cd 100644 --- a/src/gl/system/gl_wipe.cpp +++ b/src/gl/system/gl_wipe.cpp @@ -223,7 +223,7 @@ void OpenGLFrameBuffer::WipeCleanup() delete wipeendscreen; wipeendscreen = NULL; } - FMaterial::ClearLastTexture(); + gl_RenderState.ClearLastMaterial(); } //========================================================================== diff --git a/src/gl/textures/gl_hwtexture.cpp b/src/gl/textures/gl_hwtexture.cpp index db3f16fdc..2f68baaf5 100644 --- a/src/gl/textures/gl_hwtexture.cpp +++ b/src/gl/textures/gl_hwtexture.cpp @@ -36,6 +36,7 @@ #include "hwrenderer/utility/hw_cvars.h" #include "gl/system/gl_debug.h" #include "gl/renderer/gl_renderer.h" +#include "gl/renderer/gl_renderstate.h" #include "gl/textures/gl_samplers.h" @@ -469,7 +470,7 @@ void FHardwareTexture::UnbindAll() { Unbind(texunit); } - FMaterial::ClearLastTexture(); + gl_RenderState.ClearLastMaterial(); } //=========================================================================== diff --git a/src/hwrenderer/textures/hw_ihwtexture.h b/src/hwrenderer/textures/hw_ihwtexture.h index ca9d5bfad..b91291806 100644 --- a/src/hwrenderer/textures/hw_ihwtexture.h +++ b/src/hwrenderer/textures/hw_ihwtexture.h @@ -21,8 +21,6 @@ public: IHardwareTexture() {} virtual ~IHardwareTexture() {} - virtual void BindToFrameBuffer(int w, int h) = 0; - virtual bool BindOrCreate(FTexture *tex, int texunit, int clampmode, int translation, int flags) = 0; virtual void AllocateBuffer(int w, int h, int texelsize) = 0; virtual uint8_t *MapBuffer() = 0; virtual unsigned int CreateTexture(unsigned char * buffer, int w, int h, int texunit, bool mipmap, int translation, const char *name) = 0; diff --git a/src/hwrenderer/textures/hw_material.cpp b/src/hwrenderer/textures/hw_material.cpp index 23242c91c..daeca6d06 100644 --- a/src/hwrenderer/textures/hw_material.cpp +++ b/src/hwrenderer/textures/hw_material.cpp @@ -154,6 +154,12 @@ FMaterial::FMaterial(FTexture * tx, bool expanded) { if (tx->shaderindex >= FIRST_USER_SHADER) { + for (auto &texture : tx->CustomShaderTextures) + { + if(texture == nullptr) continue; + ValidateSysTexture(texture, expanded); + mTextureLayers.Push(texture); + } mShaderIndex = tx->shaderindex; } else @@ -163,7 +169,7 @@ FMaterial::FMaterial(FTexture * tx, bool expanded) for (auto &texture : { tx->Normal, tx->Specular }) { ValidateSysTexture(texture, expanded); - mTextureLayers.Push({ texture, false }); + mTextureLayers.Push(texture); } mShaderIndex = SHADER_Specular; } @@ -172,7 +178,7 @@ FMaterial::FMaterial(FTexture * tx, bool expanded) for (auto &texture : { tx->Normal, tx->Metallic, tx->Roughness, tx->AmbientOcclusion }) { ValidateSysTexture(texture, expanded); - mTextureLayers.Push({ texture, false }); + mTextureLayers.Push(texture); } mShaderIndex = SHADER_PBR; } @@ -181,8 +187,7 @@ FMaterial::FMaterial(FTexture * tx, bool expanded) if (tx->Brightmap != NULL) { ValidateSysTexture(tx->Brightmap, expanded); - FTextureLayer layer = {tx->Brightmap, false}; - mTextureLayers.Push(layer); + mTextureLayers.Push(tx->Brightmap); if (mShaderIndex == SHADER_Specular) mShaderIndex = SHADER_SpecularBrightmap; else if (mShaderIndex == SHADER_PBR) @@ -402,70 +407,6 @@ outl: return true; } - -//=========================================================================== -// -// Binds a texture to the renderer -// -//=========================================================================== - -static FMaterial *last; -static int lastclamp; -static int lasttrans; - -void FMaterial::InitGlobalState() -{ - last = nullptr; - lastclamp = 0; - lasttrans = 0; -} - -void FMaterial::Bind(int clampmode, int translation) -{ - // avoid rebinding the same texture multiple times. - if (this == last && lastclamp == clampmode && translation == lasttrans) return; - last = this; - lastclamp = clampmode; - lasttrans = translation; - - int usebright = false; - int maxbound = 0; - - if (tex->UseType == ETextureType::SWCanvas) clampmode = CLAMP_NOFILTER; - if (tex->bHasCanvas) clampmode = CLAMP_CAMTEX; - else if (tex->bWarped && clampmode <= CLAMP_XY) clampmode = CLAMP_NONE; - - // Textures that are already scaled in the texture lump will not get replaced by hires textures. - int flags = mExpanded? CTF_Expand : (gl_texture_usehires && tex->Scale.X == 1 && tex->Scale.Y == 1 && clampmode <= CLAMP_XY)? CTF_CheckHires : 0; - - if (mBaseLayer->BindOrCreate(tex, 0, clampmode, translation, flags)) - { - for(unsigned i=0;iid; - layer = TexMan(id); - } - else - { - layer = mTextureLayers[i].texture; - } - auto systex = ValidateSysTexture(layer, mExpanded); - systex->BindOrCreate(layer, i+1, clampmode, 0, mExpanded ? CTF_Expand : 0); - maxbound = i+1; - } - } - // unbind everything from the last texture that's still active - for(int i=maxbound+1; i<=mMaxBound;i++) - { - screen->UnbindTexUnit(i); - mMaxBound = maxbound; - } -} - - //=========================================================================== // // @@ -473,7 +414,7 @@ void FMaterial::Bind(int clampmode, int translation) //=========================================================================== void FMaterial::Precache() { - Bind(0, 0); + screen->PrecacheMaterial(this, 0); } //=========================================================================== @@ -486,7 +427,7 @@ void FMaterial::PrecacheList(SpriteHits &translations) if (mBaseLayer != nullptr) mBaseLayer->CleanUnused(translations); SpriteHits::Iterator it(translations); SpriteHits::Pair *pair; - while(it.NextPair(pair)) Bind(0, pair->Key); + while(it.NextPair(pair)) screen->PrecacheMaterial(this, pair->Key); } //=========================================================================== @@ -552,24 +493,6 @@ int FMaterial::GetAreas(FloatRect **pAreas) const } } -//=========================================================================== -// -// -// -//=========================================================================== - -void FMaterial::BindToFrameBuffer() -{ - if (mBaseLayer == nullptr) - { - // must create the hardware texture first - mBaseLayer->BindOrCreate(sourcetex, 0, 0, 0, 0); - screen->UnbindTexUnit(0); - ClearLastTexture(); - } - mBaseLayer->BindToFrameBuffer(mWidth, mHeight); -} - //========================================================================== // // Gets a texture from the texture manager and checks its validity for @@ -641,11 +564,6 @@ void FMaterial::FlushAll() } } -void FMaterial::ClearLastTexture() -{ - last = NULL; -} - void FMaterial::Clean(bool f) { // This somehow needs to deal with the other layers as well, but they probably need some form of reference counting to work properly... diff --git a/src/hwrenderer/textures/hw_material.h b/src/hwrenderer/textures/hw_material.h index 3ee1d935c..d334ceaa1 100644 --- a/src/hwrenderer/textures/hw_material.h +++ b/src/hwrenderer/textures/hw_material.h @@ -52,12 +52,6 @@ class FMaterial { friend class FRenderState; - struct FTextureLayer - { - FTexture *texture; - bool animated; - }; - // This array is needed because not all textures are managed by the texture manager // but some code needs to discard all hardware dependent data attached to any created texture. // Font characters are not, for example. @@ -65,7 +59,7 @@ class FMaterial static int mMaxBound; IHardwareTexture *mBaseLayer; - TArray mTextureLayers; + TArray mTextureLayers; int mShaderIndex; short mLeftOffset; @@ -81,7 +75,6 @@ class FMaterial float mSpriteU[2], mSpriteV[2]; FloatRect mSpriteRect; - IHardwareTexture * ValidateSysTexture(FTexture * tex, bool expand); bool TrimBorders(uint16_t *rect); public: @@ -93,27 +86,44 @@ public: void SetSpriteRect(); void Precache(); void PrecacheList(SpriteHits &translations); + IHardwareTexture * ValidateSysTexture(FTexture * tex, bool expand); void AddTextureLayer(FTexture *tex) { - FTextureLayer layer = { tex, false }; ValidateTexture(tex, false); - mTextureLayers.Push(layer); + mTextureLayers.Push(tex); } bool isMasked() const { return !!sourcetex->bMasked; } + bool isExpanded() const + { + return mExpanded; + } int GetLayers() const { return mTextureLayers.Size() + 1; } - void Bind(int clamp, int translation); + IHardwareTexture *GetLayer(int i, FTexture **pLayer = nullptr) + { + if (i == 0) + { + if (pLayer) *pLayer = tex; + return mBaseLayer; + } + else + { + i--; + FTexture *layer = mTextureLayers[i]; + if (pLayer) *pLayer = layer; + return ValidateSysTexture(layer, isExpanded()); + } + } void Clean(bool f); - void BindToFrameBuffer(); // Patch drawing utilities void GetSpriteRect(FloatRect * r) const @@ -172,9 +182,6 @@ public: static void FlushAll(); static FMaterial *ValidateTexture(FTexture * tex, bool expand); static FMaterial *ValidateTexture(FTextureID no, bool expand, bool trans); - static void ClearLastTexture(); - - static void InitGlobalState(); }; #endif diff --git a/src/r_data/gldefs.cpp b/src/r_data/gldefs.cpp index 4ebd8283f..b74b1de25 100644 --- a/src/r_data/gldefs.cpp +++ b/src/r_data/gldefs.cpp @@ -51,6 +51,8 @@ void AddLightAssociation(const char *actor, const char *frame, const char *light void InitializeActorLights(TArray &LightAssociations); extern TArray usershaders; +extern TArray usermaterials; +extern TArray usershaderdefs; extern TDeletingArray LightDefaults; @@ -1378,6 +1380,9 @@ class GLDefsParser bool iwad = false; int maplump = -1; FString maplumpname; + FString materiallumpname = "shaders/glsl/material_normal.fp"; + FString texnameDefs = ""; + TArray texNameList; float speed = 1.f; sc.MustGetString(); @@ -1393,11 +1398,62 @@ class GLDefsParser sc.MustGetString(); maplumpname = sc.String; } + else if (sc.Compare("material")) + { + sc.MustGetString(); + materiallumpname = sc.String; + } else if (sc.Compare("speed")) { sc.MustGetFloat(); speed = float(sc.Float); } + else if (sc.Compare("texture")) + { + sc.MustGetString(); + FString textureName = sc.String; + for(FString &texName : texNameList) + { + if(!texName.Compare(textureName)) + { + sc.ScriptError("Trying to redefine custom hardware shader texture '%s' in texture '%s'\n", textureName.GetChars(), tex? tex->Name.GetChars() : "(null)"); + } + } + texNameList.Push(textureName); + sc.MustGetString(); + bool okay = false; + for (int i = 0; i < MAX_CUSTOM_HW_SHADER_TEXTURES; i++) + { + if (!tex->CustomShaderTextures[i]) + { + tex->CustomShaderTextures[i] = TexMan.FindTexture(sc.String, ETextureType::Any, FTextureManager::TEXMAN_TryAny); + if (!tex->CustomShaderTextures[i]) + { + sc.ScriptError("Custom hardware shader texture '%s' not found in texture '%s'\n", sc.String, tex? tex->Name.GetChars() : "(null)"); + } + + texnameDefs.AppendFormat("#define %s texture%d\n", textureName.GetChars(), i + 2); + okay = true; + break; + } + } + if(!okay) + { + sc.ScriptError("Error: out of texture units in texture '%s'", tex? tex->Name.GetChars() : "(null)"); + } + } + else if(sc.Compare("define")) + { + sc.MustGetString(); + FString defineName = sc.String; + FString defineValue = ""; + if(sc.CheckToken('=')) + { + sc.MustGetString(); + defineValue = sc.String; + } + texnameDefs.AppendFormat("#define %s %s\n", defineName.GetChars(), defineValue.GetChars()); + } } if (!tex) { @@ -1414,13 +1470,17 @@ class GLDefsParser tex->shaderspeed = speed; for (unsigned i = 0; i < usershaders.Size(); i++) { - if (!usershaders[i].CompareNoCase(maplumpname)) + if (!usershaders[i].CompareNoCase(maplumpname) && + !usermaterials[i].CompareNoCase(materiallumpname) && + !usershaderdefs[i].Compare(texnameDefs)) { tex->shaderindex = i + FIRST_USER_SHADER; return; } } tex->shaderindex = usershaders.Push(maplumpname) + FIRST_USER_SHADER; + usermaterials.Push(materiallumpname); + usershaderdefs.Push(texnameDefs); } } } diff --git a/src/textures/textures.h b/src/textures/textures.h index 4b175aee4..3b3f0a20d 100644 --- a/src/textures/textures.h +++ b/src/textures/textures.h @@ -44,6 +44,9 @@ #include "r_data/r_translate.h" #include +// 15 because 0th texture is our texture +#define MAX_CUSTOM_HW_SHADER_TEXTURES 15 + typedef TMap SpriteHits; enum MaterialShaderIndex @@ -234,6 +237,8 @@ public: FTexture *Metallic = nullptr; // Metalness texture for the physically based rendering (PBR) light model FTexture *Roughness = nullptr; // Roughness texture for PBR FTexture *AmbientOcclusion = nullptr; // Ambient occlusion texture for PBR + + FTexture *CustomShaderTextures[MAX_CUSTOM_HW_SHADER_TEXTURES] = { nullptr }; // Custom texture maps for custom hardware shaders FString Name; ETextureType UseType; // This texture's primary purpose diff --git a/src/v_video.h b/src/v_video.h index 2480a5e32..f4ac30092 100644 --- a/src/v_video.h +++ b/src/v_video.h @@ -417,6 +417,7 @@ public: virtual void CleanForRestart() {} virtual void SetTextureFilterMode() {} virtual IHardwareTexture *CreateHardwareTexture(FTexture *tex) { return nullptr; } + virtual void PrecacheMaterial(FMaterial *mat, int translation) {} virtual FModelRenderer *CreateModelRenderer(int mli) { return nullptr; } virtual void UnbindTexUnit(int no) {} virtual void TextureFilterChanged() {}