diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 95c07ac7d..a89a6cc7d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1070,6 +1070,7 @@ set (PCH_SOURCES common/textures/bitmap.cpp common/textures/m_png.cpp common/textures/texture.cpp + common/textures/gametexture.cpp common/textures/image.cpp common/textures/imagetexture.cpp common/textures/texturemanager.cpp diff --git a/src/common/textures/gametexture.cpp b/src/common/textures/gametexture.cpp new file mode 100644 index 000000000..1ec6a6062 --- /dev/null +++ b/src/common/textures/gametexture.cpp @@ -0,0 +1,512 @@ +/* +** gametexture.cpp +** The game-facing texture class. +** +**--------------------------------------------------------------------------- +** Copyright 2004-2007 Randy Heit +** Copyright 2006-2020 Christoph Oelckers +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +** +*/ + +#include "printf.h" +#include "files.h" +#include "filesystem.h" +#include "templates.h" +#include "textures.h" +#include "bitmap.h" +#include "colormatcher.h" +#include "c_dispatch.h" +#include "m_fixed.h" +#include "imagehelpers.h" +#include "image.h" +#include "formats/multipatchtexture.h" +#include "texturemanager.h" +#include "c_cvars.h" +#include "hw_material.h" + +FTexture *CreateBrightmapTexture(FImageSource*); + + +FGameTexture::FGameTexture(FTexture* wrap, const char* name) : Name(name) +{ + if (wrap) Setup(wrap); +} + +//========================================================================== +// +// +// +//========================================================================== + +void FGameTexture::Setup(FTexture *wrap) +{ + Base = wrap; + id.SetInvalid(); + TexelWidth = Base->GetWidth(); + DisplayWidth = (float)TexelWidth; + TexelHeight = Base->GetHeight(); + DisplayHeight = (float)TexelHeight; + auto img = Base->GetImage(); + if (img) + { + auto ofs = img->GetOffsets(); + LeftOffset[0] = LeftOffset[1] = ofs.first; + TopOffset[0] = TopOffset[1] = ofs.second; + } + else + { + LeftOffset[0] = LeftOffset[1] = + TopOffset[0] = TopOffset[1] = 0; + + } + ScaleX = ScaleY = 1.f; +} + +//========================================================================== +// +// +// +//========================================================================== + +FGameTexture::~FGameTexture() +{ + FGameTexture* link = fileSystem.GetLinkedTexture(GetSourceLump()); + if (link == this) fileSystem.SetLinkedTexture(GetSourceLump(), nullptr); + if (SoftwareTexture != nullptr) + { + delete SoftwareTexture; + SoftwareTexture = nullptr; + } + for (auto &mat : Material) + { + if (mat != nullptr) delete mat; + mat = nullptr; + } + +} + +//========================================================================== +// +// +// +//========================================================================== + +bool FGameTexture::isUserContent() const +{ + int filenum = fileSystem.GetFileContainer(Base->GetSourceLump()); + return (filenum > fileSystem.GetMaxIwadNum()); +} + + +//========================================================================== +// +// Search auto paths for extra material textures +// +//========================================================================== + +void FGameTexture::AddAutoMaterials() +{ + struct AutoTextureSearchPath + { + const char* path; + RefCountedPtr FGameTexture::* pointer; + }; + + static AutoTextureSearchPath autosearchpaths[] = + { + { "brightmaps/", &FGameTexture::Brightmap }, // For backwards compatibility, only for short names + { "materials/brightmaps/", &FGameTexture::Brightmap }, + { "materials/normalmaps/", &FGameTexture::Normal }, + { "materials/specular/", &FGameTexture::Specular }, + { "materials/metallic/", &FGameTexture::Metallic }, + { "materials/roughness/", &FGameTexture::Roughness }, + { "materials/ao/", &FGameTexture::AmbientOcclusion } + }; + + + bool fullname = !!(flags & GTexf_FullNameTexture); + FString searchname = GetName(); + + if (fullname) + { + auto dot = searchname.LastIndexOf('.'); + auto slash = searchname.LastIndexOf('/'); + if (dot > slash) searchname.Truncate(dot); + } + + for (size_t i = 0; i < countof(autosearchpaths); i++) + { + auto& layer = autosearchpaths[i]; + if (this->*(layer.pointer) == nullptr) // only if no explicit assignment had been done. + { + FStringf lookup("%s%s%s", layer.path, fullname ? "" : "auto/", searchname.GetChars()); + auto lump = fileSystem.CheckNumForFullName(lookup, false, ns_global, true); + if (lump != -1) + { + auto bmtex = TexMan.FindGameTexture(fileSystem.GetFileFullName(lump), ETextureType::Any, FTextureManager::TEXMAN_TryAny); + if (bmtex != nullptr) + { + this->*(layer.pointer) = bmtex->GetTexture(); + } + } + } + } +} + +//=========================================================================== +// +// Checks if the texture has a default brightmap and creates it if so +// +//=========================================================================== +void FGameTexture::CreateDefaultBrightmap() +{ + auto tex = GetTexture(); + if (flags & GTexf_BrightmapChecked) + { + flags |= GTexf_BrightmapChecked; + // Check for brightmaps + if (tex->GetImage() && tex->GetImage()->UseGamePalette() && GPalette.HasGlobalBrightmap && + GetUseType() != ETextureType::Decal && GetUseType() != ETextureType::MiscPatch && GetUseType() != ETextureType::FontChar && + Brightmap == nullptr) + { + // May have one - let's check when we use this texture + auto texbuf = tex->Get8BitPixels(false); + const int white = ColorMatcher.Pick(255, 255, 255); + + int size = tex->GetWidth() * tex->GetHeight(); + for (int i = 0; i < size; i++) + { + if (GPalette.GlobalBrightmap.Remap[texbuf[i]] == white) + { + // Create a brightmap + DPrintf(DMSG_NOTIFY, "brightmap created for texture '%s'\n", GetName().GetChars()); + Brightmap = CreateBrightmapTexture(tex->GetImage()); + return; + } + } + // No bright pixels found + DPrintf(DMSG_SPAMMY, "No bright pixels found in texture '%s'\n", GetName().GetChars()); + } + } +} + + +//========================================================================== +// +// Calculates glow color for a texture +// +//========================================================================== + +void FGameTexture::GetGlowColor(float* data) +{ + if (isGlowing() && GlowColor == 0) + { + auto buffer = Base->GetBgraBitmap(nullptr); + GlowColor = averageColor((uint32_t*)buffer.GetPixels(), buffer.GetWidth() * buffer.GetHeight(), 153); + + // Black glow equals nothing so switch glowing off + if (GlowColor == 0) flags &= ~GTexf_Glowing; + } + data[0] = GlowColor.r * (1 / 255.0f); + data[1] = GlowColor.g * (1 / 255.0f); + data[2] = GlowColor.b * (1 / 255.0f); +} + +//=========================================================================== +// +// +// +//=========================================================================== + +int FGameTexture::GetAreas(FloatRect** pAreas) const +{ + if (shaderindex == SHADER_Default) // texture splitting can only be done if there's no attached effects + { + *pAreas = Base->areas; + return Base->areacount; + } + else + { + return 0; + } +} + +//=========================================================================== +// +// Checks if a sprite may be expanded with an empty frame +// +//=========================================================================== + +bool FGameTexture::ShouldExpandSprite() +{ + if (expandSprite != -1) return expandSprite; + // Only applicable to image textures with no shader effect. + if (GetShaderIndex() != SHADER_Default || !dynamic_cast(Base.get())) + { + expandSprite = false; + return false; + } + if (Brightmap != NULL && (Base->GetWidth() != Brightmap->GetWidth() || Base->GetHeight() != Brightmap->GetHeight())) + { + // do not expand if the brightmap's physical size differs from the base. + expandSprite = false; + return false; + } + if (Glowmap != NULL && (Base->GetWidth() != Glowmap->GetWidth() || Base->GetHeight() != Glowmap->GetHeight())) + { + // same restriction for the glow map + expandSprite = false; + return false; + } + expandSprite = true; + return true; +} + +//=========================================================================== +// +// Sets up the sprite positioning data for this texture +// +//=========================================================================== + +void FGameTexture::SetupSpriteData() +{ + // Since this is only needed for real sprites it gets allocated on demand. + // It also allocates from the image memory arena because it has the same lifetime and to reduce maintenance. + if (spi == nullptr) spi = (SpritePositioningInfo*)ImageArena.Alloc(2 * sizeof(SpritePositioningInfo)); + for (int i = 0; i < 2; i++) + { + auto& spi = this->spi[i]; + spi.mSpriteU[0] = spi.mSpriteV[0] = 0.f; + spi.mSpriteU[1] = spi.mSpriteV[1] = 1.f; + spi.spriteWidth = GetTexelWidth(); + spi.spriteHeight = GetTexelHeight(); + + if (i == 1 && ShouldExpandSprite()) + { + spi.mTrimResult = Base->TrimBorders(spi.trim); // get the trim size before adding the empty frame + spi.spriteWidth += 2; + spi.spriteHeight += 2; + } + } + SetSpriteRect(); +} + +//=========================================================================== +// +// Set the sprite rectangle. This is separate because it may be called by a CVAR, too. +// +//=========================================================================== + +void FGameTexture::SetSpriteRect() +{ + + if (!spi) return; + auto leftOffset = GetTexelLeftOffset(r_spriteadjustHW); + auto topOffset = GetTexelTopOffset(r_spriteadjustHW); + + float fxScale = GetScaleX(); + float fyScale = GetScaleY(); + + for (int i = 0; i < 2; i++) + { + auto& spi = this->spi[i]; + + // mSpriteRect is for positioning the sprite in the scene. + spi.mSpriteRect.left = -leftOffset / fxScale; + spi.mSpriteRect.top = -topOffset / fyScale; + spi.mSpriteRect.width = spi.spriteWidth / fxScale; + spi.mSpriteRect.height = spi.spriteHeight / fyScale; + + if (i == 1 && ShouldExpandSprite()) + { + // a little adjustment to make sprites look better with texture filtering: + // create a 1 pixel wide empty frame around them. + + int oldwidth = spi.spriteWidth - 2; + int oldheight = spi.spriteHeight - 2; + + leftOffset += 1; + topOffset += 1; + + // Reposition the sprite with the frame considered + spi.mSpriteRect.left = -(float)leftOffset / fxScale; + spi.mSpriteRect.top = -(float)topOffset / fyScale; + spi.mSpriteRect.width = (float)spi.spriteWidth / fxScale; + spi.mSpriteRect.height = (float)spi.spriteHeight / fyScale; + + if (spi.mTrimResult > 0) + { + spi.mSpriteRect.left += (float)spi.trim[0] / fxScale; + spi.mSpriteRect.top += (float)spi.trim[1] / fyScale; + + spi.mSpriteRect.width -= float(oldwidth - spi.trim[2]) / fxScale; + spi.mSpriteRect.height -= float(oldheight - spi.trim[3]) / fyScale; + + spi.mSpriteU[0] = (float)spi.trim[0] / (float)spi.spriteWidth; + spi.mSpriteV[0] = (float)spi.trim[1] / (float)spi.spriteHeight; + spi.mSpriteU[1] -= float(oldwidth - spi.trim[0] - spi.trim[2]) / (float)spi.spriteWidth; + spi.mSpriteV[1] -= float(oldheight - spi.trim[1] - spi.trim[3]) / (float)spi.spriteHeight; + } + } + } +} + +//=========================================================================== +// +// Cleans the attached hardware resources. +// This should only be used on textures which alter their content at run time +//or when the engine shuts down. +// +//=========================================================================== + +void FGameTexture::CleanHardwareData(bool full) +{ + Base->CleanHardwareTextures(); + for (auto mat : Material) if (mat) mat->DeleteDescriptors(); +} + + +//----------------------------------------------------------------------------- +// +// Make sprite offset adjustment user-configurable per renderer. +// +//----------------------------------------------------------------------------- + +CUSTOM_CVAR(Int, r_spriteadjust, 2, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) +{ + r_spriteadjustHW = !!(self & 2); + r_spriteadjustSW = !!(self & 1); + for (int i = 0; i < TexMan.NumTextures(); i++) + { + auto tex = TexMan.GetGameTexture(FSetTextureID(i)); + if (tex->GetTexelLeftOffset(0) != tex->GetTexelLeftOffset(1) || tex->GetTexelTopOffset(0) != tex->GetTexelTopOffset(1)) + { + tex->SetSpriteRect(); + } + } +} + +//=========================================================================== +// +// Coordinate helper. +// The only reason this is even needed is that many years ago someone +// was convinced that having per-texel panning on walls was a good idea. +// If it wasn't for this relatively useless feature the entire positioning +// code for wall textures could be a lot simpler. +// +//=========================================================================== + +//=========================================================================== +// +// +// +//=========================================================================== + +float FTexCoordInfo::RowOffset(float rowoffset) const +{ + float scale = fabs(mScale.Y); + if (scale == 1.f || mWorldPanning) return rowoffset; + else return rowoffset / scale; +} + +//=========================================================================== +// +// +// +//=========================================================================== + +float FTexCoordInfo::TextureOffset(float textureoffset) const +{ + float scale = fabs(mScale.X); + if (scale == 1.f || mWorldPanning) return textureoffset; + else return textureoffset / scale; +} + +//=========================================================================== +// +// Returns the size for which texture offset coordinates are used. +// +//=========================================================================== + +float FTexCoordInfo::TextureAdjustWidth() const +{ + if (mWorldPanning) + { + float tscale = fabs(mTempScale.X); + if (tscale == 1.f) return (float)mRenderWidth; + else return mWidth / fabs(tscale); + } + else return (float)mWidth; +} + + +//=========================================================================== +// +// Retrieve texture coordinate info for per-wall scaling +// +//=========================================================================== + +void FTexCoordInfo::GetFromTexture(FGameTexture *tex, float x, float y, bool forceworldpanning) +{ + if (x == 1.f) + { + mRenderWidth = xs_RoundToInt(tex->GetDisplayWidth()); + mScale.X = tex->GetScaleX(); + mTempScale.X = 1.f; + } + else + { + float scale_x = x * tex->GetScaleX(); + mRenderWidth = xs_CeilToInt(tex->GetTexelWidth() / scale_x); + mScale.X = scale_x; + mTempScale.X = x; + } + + if (y == 1.f) + { + mRenderHeight = xs_RoundToInt(tex->GetDisplayHeight()); + mScale.Y = tex->GetScaleY(); + mTempScale.Y = 1.f; + } + else + { + float scale_y = y * tex->GetScaleY(); + mRenderHeight = xs_CeilToInt(tex->GetTexelHeight() / scale_y); + mScale.Y = scale_y; + mTempScale.Y = y; + } + if (tex->isHardwareCanvas()) + { + mScale.Y = -mScale.Y; + mRenderHeight = -mRenderHeight; + } + mWorldPanning = tex->useWorldPanning() || forceworldpanning; + mWidth = tex->GetTexelWidth(); +} + diff --git a/src/common/textures/gametexture.h b/src/common/textures/gametexture.h new file mode 100644 index 000000000..f99c45b74 --- /dev/null +++ b/src/common/textures/gametexture.h @@ -0,0 +1,316 @@ +#pragma once +#include +#include "vectors.h" +#include "floatrect.h" +#include "refcounted.h" +#include "xs_Float.h" +#include "palentry.h" +#include "zstring.h" +#include "textureid.h" + +// 15 because 0th texture is our texture +#define MAX_CUSTOM_HW_SHADER_TEXTURES 15 +class FTexture; +class ISoftwareTexture; +class FMaterial; + +struct SpritePositioningInfo +{ + uint16_t trim[4]; + int spriteWidth, spriteHeight; + float mSpriteU[2], mSpriteV[2]; + FloatRect mSpriteRect; + uint8_t mTrimResult; + + float GetSpriteUL() const { return mSpriteU[0]; } + float GetSpriteVT() const { return mSpriteV[0]; } + float GetSpriteUR() const { return mSpriteU[1]; } + float GetSpriteVB() const { return mSpriteV[1]; } + + const FloatRect &GetSpriteRect() const + { + return mSpriteRect; + } + +}; + +struct MaterialLayers +{ + float Glossiness; + float SpecularLevel; + FGameTexture* Brightmap; + FGameTexture* Normal; + FGameTexture* Specular; + FGameTexture* Metallic; + FGameTexture* Roughness; + FGameTexture* AmbientOcclusion; + FGameTexture* CustomShaderTextures[MAX_CUSTOM_HW_SHADER_TEXTURES]; +}; + +enum EGameTexFlags +{ + GTexf_NoDecals = 1, // Decals should not stick to texture + GTexf_WorldPanning = 2, // Texture is panned in world units rather than texels + GTexf_FullNameTexture = 4, // Name is taken from the file system. + GTexf_Glowing = 8, // Texture emits a glow + GTexf_AutoGlowing = 16, // Glow info is determined from texture image. + GTexf_RenderFullbright = 32, // always draw fullbright + GTexf_DisableFullbrightSprites = 64, // This texture will not be displayed as fullbright sprite + GTexf_BrightmapChecked = 128, // Check for a colormap-based brightmap was already done. +}; + +// Refactoring helper to allow piece by piece adjustment of the API +class FGameTexture +{ + friend class FMaterial; + + // Material layers. These are shared so reference counting is used. + RefCountedPtr Base; + RefCountedPtr Brightmap; + RefCountedPtr Detailmap; + RefCountedPtr Glowmap; + RefCountedPtr Normal; // Normal map texture + RefCountedPtr Specular; // Specular light texture for the diffuse+normal+specular light model + RefCountedPtr Metallic; // Metalness texture for the physically based rendering (PBR) light model + RefCountedPtr Roughness; // Roughness texture for PBR + RefCountedPtr AmbientOcclusion; // Ambient occlusion texture for PBR + RefCountedPtr CustomShaderTextures[MAX_CUSTOM_HW_SHADER_TEXTURES]; // Custom texture maps for custom hardware shaders + + FString Name; + FTextureID id; + + uint16_t TexelWidth, TexelHeight; + int16_t LeftOffset[2], TopOffset[2]; + float DisplayWidth, DisplayHeight; + float ScaleX, ScaleY; + + int8_t shouldUpscaleFlag = 0; // Without explicit setup, scaling is disabled for a texture. + ETextureType UseType = ETextureType::Wall; // This texture's primary purpose + SpritePositioningInfo* spi = nullptr; + + ISoftwareTexture* SoftwareTexture = nullptr; + FMaterial* Material[4] = { }; + + // Material properties + float Glossiness = 10.f; + float SpecularLevel = 0.1f; + float shaderspeed = 1.f; + int shaderindex = 0; + + int flags = 0; + uint8_t warped = 0, expandSprite = -1; + uint16_t GlowHeight; + PalEntry GlowColor = 0; + + int16_t SkyOffset = 0; + uint16_t Rotations = 0xffff; + + +public: + FGameTexture(FTexture* wrap, const char *name); + ~FGameTexture(); + void Setup(FTexture* wrap); + FTextureID GetID() const { return id; } + void SetID(FTextureID newid) { id = newid; } // should only be called by the texture manager + const FString& GetName() const { return Name; } + void SetName(const char* name) { Name = name; } // should only be called by setup code. + + float GetScaleX() { return ScaleX; } + float GetScaleY() { return ScaleY; } + float GetDisplayWidth() const { return DisplayWidth; } + float GetDisplayHeight() const { return DisplayHeight; } + int GetTexelWidth() const { return TexelWidth; } + int GetTexelHeight() const { return TexelHeight; } + + void CreateDefaultBrightmap(); + void AddAutoMaterials(); + bool ShouldExpandSprite(); + void SetupSpriteData(); + void SetSpriteRect(); + + ETextureType GetUseType() const { return UseType; } + void SetUpscaleFlag(int what) { shouldUpscaleFlag = what; } + int GetUpscaleFlag() { return shouldUpscaleFlag; } + + FTexture* GetTexture() { return Base.get(); } + int GetSourceLump() const { return Base->GetSourceLump(); } + void SetBrightmap(FGameTexture* tex) { Brightmap = tex->GetTexture(); } + + int GetTexelLeftOffset(int adjusted = 0) const { return LeftOffset[adjusted]; } + int GetTexelTopOffset(int adjusted = 0) const { return TopOffset[adjusted]; } + float GetDisplayLeftOffset(int adjusted = 0) const { return LeftOffset[adjusted] / ScaleX; } + float GetDisplayTopOffset(int adjusted = 0) const { return TopOffset[adjusted] / ScaleY; } + + bool isMiscPatch() const { return GetUseType() == ETextureType::MiscPatch; } // only used by the intermission screen to decide whether to tile the background image or not. + bool isFullbrightDisabled() const { return !!(flags & GTexf_DisableFullbrightSprites); } + bool isFullbright() const { return !!(flags & GTexf_RenderFullbright); } + bool isFullNameTexture() const { return !!(flags & GTexf_FullNameTexture); } + bool expandSprites() const { return !!expandSprite; } + bool useWorldPanning() const { return !!(flags & GTexf_WorldPanning); } + void SetWorldPanning(bool on) { if (on) flags |= GTexf_WorldPanning; else flags &= ~GTexf_WorldPanning; } + bool allowNoDecals() const { return !!(flags & GTexf_NoDecals); } + void SetNoDecals(bool on) { if (on) flags |= GTexf_NoDecals; else flags &= ~GTexf_NoDecals; } + + bool isValid() const { return UseType != ETextureType::Null; } + int isWarped() { return warped; } + void SetWarpStyle(int style) { warped = style; } + bool isMasked() { return Base->Masked; } + bool isHardwareCanvas() const { return Base->isHardwareCanvas(); } // There's two here so that this can deal with software canvases in the hardware renderer later. + bool isSoftwareCanvas() const { return Base->isCanvas(); } + + void SetTranslucent(bool on) { Base->bTranslucent = on; } + void SetUseType(ETextureType type) { UseType = type; } + int GetRotations() const { return Rotations; } + void SetRotations(int rot) { Rotations = int16_t(rot); } + void SetSkyOffset(int offs) { SkyOffset = offs; } + int GetSkyOffset() const { return SkyOffset; } + + ISoftwareTexture* GetSoftwareTexture() + { + return SoftwareTexture; + } + void SetSoftwareTexture(ISoftwareTexture* swtex) + { + SoftwareTexture = swtex; + } + + FMaterial* GetMaterial(int num) + { + return Material[num]; + } + + int GetShaderIndex() const { return shaderindex; } + float GetShaderSpeed() const { return shaderspeed; } + void SetShaderSpeed(float speed) { shaderspeed = speed; } + void SetShaderIndex(int index) { shaderindex = index; } + void SetShaderLayers(MaterialLayers& lay) + { + // Only update layers that have something defind. + if (lay.Glossiness > -1000) Glossiness = lay.Glossiness; + if (lay.SpecularLevel > -1000) SpecularLevel = lay.SpecularLevel; + if (lay.Brightmap) Brightmap = lay.Brightmap->GetTexture(); + if (lay.Normal) Normal = lay.Normal->GetTexture(); + if (lay.Specular) Specular = lay.Specular->GetTexture(); + if (lay.Metallic) Metallic = lay.Metallic->GetTexture(); + if (lay.Roughness) Roughness = lay.Roughness->GetTexture(); + if (lay.AmbientOcclusion) AmbientOcclusion = lay.AmbientOcclusion->GetTexture(); + for (int i = 0; i < MAX_CUSTOM_HW_SHADER_TEXTURES; i++) + { + if (lay.CustomShaderTextures[i]) CustomShaderTextures[i] = lay.CustomShaderTextures[i]->GetTexture(); + } + } + float GetGlossiness() const { return Glossiness; } + float GetSpecularLevel() const { return SpecularLevel; } + + void CopySize(FGameTexture* BaseTexture) + { + Base->CopySize(BaseTexture->Base.get()); + } + + // Glowing is a pure material property that should not filter down to the actual texture objects. + void GetGlowColor(float* data); + bool isGlowing() const { return !!(flags & GTexf_Glowing); } + bool isAutoGlowing() const { return !!(flags & GTexf_AutoGlowing); } + int GetGlowHeight() const { return GlowHeight; } + void SetAutoGlowing() { flags |= (GTexf_AutoGlowing | GTexf_Glowing | GTexf_RenderFullbright); } + void SetGlowHeight(int v) { GlowHeight = v; } + void SetFullbright() { flags |= GTexf_RenderFullbright; } + void SetDisableFullbright(bool on) { if (on) flags |= GTexf_DisableFullbrightSprites; else flags &= ~GTexf_DisableFullbrightSprites; } + void SetGlowing(PalEntry color) { flags = (flags & ~GTexf_AutoGlowing) | GTexf_Glowing; GlowColor = color; } + + bool isUserContent() const; + int CheckRealHeight() { return xs_RoundToInt(Base->CheckRealHeight() / ScaleY); } + void SetSize(int x, int y) + { + TexelWidth = x; + TexelHeight = y; + SetDisplaySize(float(x), float(y)); + } + void SetDisplaySize(float w, float h) + { + DisplayWidth = w; + DisplayHeight = h; + ScaleX = TexelWidth / w; + ScaleY = TexelHeight / h; + + // compensate for roundoff errors + if (int(ScaleX * w) != TexelWidth) ScaleX += (1 / 65536.); + if (int(ScaleY * h) != TexelHeight) ScaleY += (1 / 65536.); + + } + void SetOffsets(int which, int x, int y) + { + LeftOffset[which] = x; + TopOffset[which] = y; + } + void SetScale(float x, float y) + { + ScaleX = x; + ScaleY = y; + DisplayWidth = x * TexelWidth; + DisplayHeight = y * TexelHeight; + } + + const SpritePositioningInfo& GetSpritePositioning(int which) { if (spi == nullptr) SetupSpriteData(); return spi[which]; } + int GetAreas(FloatRect** pAreas) const; + + bool GetTranslucency() + { + return Base->GetTranslucency(); + } + + int GetClampMode(int clampmode) + { + if (GetUseType() == ETextureType::SWCanvas) clampmode = CLAMP_NOFILTER; + else if (isHardwareCanvas()) clampmode = CLAMP_CAMTEX; + else if ((isWarped() || shaderindex >= FIRST_USER_SHADER) && clampmode <= CLAMP_XY) clampmode = CLAMP_NONE; + return clampmode; + } + + void CleanHardwareData(bool full = true); + +}; + +inline FGameTexture* MakeGameTexture(FTexture* tex, const char *name, ETextureType useType) +{ + if (!tex) return nullptr; + auto t = new FGameTexture(tex, name); + t->SetUseType(useType); + return t; +} + +enum EUpscaleFlags +{ + UF_None = 0, + UF_Texture = 1, + UF_Sprite = 2, + UF_Font = 4 +}; + +extern int upscalemask; +void UpdateUpscaleMask(); + +int calcShouldUpscale(FGameTexture* tex); +inline int shouldUpscale(FGameTexture* tex, EUpscaleFlags UseType) +{ + // This only checks the global scale mask and the texture's validation for upscaling. Everything else has been done up front elsewhere. + if (!(upscalemask & UseType)) return 0; + return tex->GetUpscaleFlag(); +} + +struct FTexCoordInfo +{ + int mRenderWidth; + int mRenderHeight; + int mWidth; + FVector2 mScale; + FVector2 mTempScale; + bool mWorldPanning; + + float FloatToTexU(float v) const { return v / mRenderWidth; } + float FloatToTexV(float v) const { return v / mRenderHeight; } + float RowOffset(float ofs) const; + float TextureOffset(float ofs) const; + float TextureAdjustWidth() const; + void GetFromTexture(FGameTexture* tex, float x, float y, bool forceworldpanning); +}; diff --git a/src/common/textures/texture.cpp b/src/common/textures/texture.cpp index fcf1d871d..87f17708c 100644 --- a/src/common/textures/texture.cpp +++ b/src/common/textures/texture.cpp @@ -49,39 +49,12 @@ #include "texturemanager.h" #include "c_cvars.h" -EXTERN_CVAR(Int, gl_texture_hqresize_targets) - // Wrappers to keep the definitions of these classes out of here. -void DeleteMaterial(FMaterial* mat); IHardwareTexture* CreateHardwareTexture(); - -FTexture *CreateBrightmapTexture(FImageSource*); - // Make sprite offset adjustment user-configurable per renderer. int r_spriteadjustSW, r_spriteadjustHW; -//========================================================================== -// -// -// -//========================================================================== - - -// Examines the lump contents to decide what type of texture to create, -// and creates the texture. -FTexture * FTexture::CreateTexture(int lumpnum, bool allowflats) -{ - if (lumpnum == -1) return nullptr; - - auto image = FImageSource::GetImage(lumpnum, allowflats); - if (image != nullptr) - { - return new FImageTexture(image); - } - return nullptr; -} - //========================================================================== // // @@ -136,139 +109,6 @@ int FTexture::CheckRealHeight() return 0; } -//========================================================================== -// -// Search auto paths for extra material textures -// -//========================================================================== - -void FGameTexture::AddAutoMaterials() -{ - struct AutoTextureSearchPath - { - const char *path; - RefCountedPtr FGameTexture::*pointer; - }; - - static AutoTextureSearchPath autosearchpaths[] = - { - { "brightmaps/", &FGameTexture::Brightmap }, // For backwards compatibility, only for short names - { "materials/brightmaps/", &FGameTexture::Brightmap }, - { "materials/normalmaps/", &FGameTexture::Normal }, - { "materials/specular/", &FGameTexture::Specular }, - { "materials/metallic/", &FGameTexture::Metallic }, - { "materials/roughness/", &FGameTexture::Roughness }, - { "materials/ao/", &FGameTexture::AmbientOcclusion } - }; - - - bool fullname = !!(flags & GTexf_FullNameTexture); - FString searchname = GetName(); - - if (fullname) - { - auto dot = searchname.LastIndexOf('.'); - auto slash = searchname.LastIndexOf('/'); - if (dot > slash) searchname.Truncate(dot); - } - - for (size_t i = 0; i < countof(autosearchpaths); i++) - { - auto &layer = autosearchpaths[i]; - if (this->*(layer.pointer) == nullptr) // only if no explicit assignment had been done. - { - FStringf lookup("%s%s%s", layer.path, fullname ? "" : "auto/", searchname.GetChars()); - auto lump = fileSystem.CheckNumForFullName(lookup, false, ns_global, true); - if (lump != -1) - { - auto bmtex = TexMan.FindGameTexture(fileSystem.GetFileFullName(lump), ETextureType::Any, FTextureManager::TEXMAN_TryAny); - if (bmtex != nullptr) - { - this->*(layer.pointer) = bmtex->GetTexture(); - } - } - } - } -} - -//=========================================================================== -// -// Checks if the texture has a default brightmap and creates it if so -// -//=========================================================================== -void FGameTexture::CreateDefaultBrightmap() -{ - auto tex = GetTexture(); - if (flags & GTexf_BrightmapChecked) - { - flags |= GTexf_BrightmapChecked; - // Check for brightmaps - if (tex->GetImage() && tex->GetImage()->UseGamePalette() && GPalette.HasGlobalBrightmap && - GetUseType() != ETextureType::Decal && GetUseType() != ETextureType::MiscPatch && GetUseType() != ETextureType::FontChar && - Brightmap == nullptr) - { - // May have one - let's check when we use this texture - auto texbuf = tex->Get8BitPixels(false); - const int white = ColorMatcher.Pick(255, 255, 255); - - int size = tex->GetWidth() * tex->GetHeight(); - for (int i = 0; iGetImage()); - return; - } - } - // No bright pixels found - DPrintf(DMSG_SPAMMY, "No bright pixels found in texture '%s'\n", GetName().GetChars()); - } - } -} - - -//========================================================================== -// -// Calculates glow color for a texture -// -//========================================================================== - -void FGameTexture::GetGlowColor(float *data) -{ - if (isGlowing() && GlowColor == 0) - { - auto buffer = Base->GetBgraBitmap(nullptr); - GlowColor = averageColor((uint32_t*)buffer.GetPixels(), buffer.GetWidth() * buffer.GetHeight(), 153); - - // Black glow equals nothing so switch glowing off - if (GlowColor == 0) flags &= ~GTexf_Glowing; - } - data[0] = GlowColor.r * (1/255.0f); - data[1] = GlowColor.g * (1/255.0f); - data[2] = GlowColor.b * (1/255.0f); -} - -//=========================================================================== -// -// -// -//=========================================================================== - -int FGameTexture::GetAreas(FloatRect** pAreas) const -{ - if (shaderindex == SHADER_Default) // texture splitting can only be done if there's no attached effects - { - *pAreas = Base->areas; - return Base->areacount; - } - else - { - return 0; - } -} - //=========================================================================== // // Finds gaps in the texture which can be skipped by the renderer @@ -576,37 +416,6 @@ TArray FTexture::Get8BitPixels(bool alphatex) return Pixels; } -//=========================================================================== -// -// Checks if a sprite may be expanded with an empty frame -// -//=========================================================================== - -bool FGameTexture::ShouldExpandSprite() -{ - if (expandSprite != -1) return expandSprite; - // Only applicable to image textures with no shader effect. - if (GetShaderIndex() != SHADER_Default || !dynamic_cast(Base.get())) - { - expandSprite = false; - return false; - } - if (Brightmap != NULL && (Base->GetWidth() != Brightmap->GetWidth() || Base->GetHeight() != Brightmap->GetHeight())) - { - // do not expand if the brightmap's physical size differs from the base. - expandSprite = false; - return false; - } - if (Glowmap != NULL && (Base->GetWidth() != Glowmap->GetWidth() || Base->GetHeight() != Glowmap->GetHeight())) - { - // same restriction for the glow map - expandSprite = false; - return false; - } - expandSprite = true; - return true; -} - //=========================================================================== // // Finds empty space around the texture. @@ -697,101 +506,6 @@ outl: return true; } -//=========================================================================== -// -// Sets up the sprite positioning data for this texture -// -//=========================================================================== - -void FGameTexture::SetupSpriteData() -{ - // Since this is only needed for real sprites it gets allocated on demand. - // It also allocates from the image memory arena because it has the same lifetime and to reduce maintenance. - if (spi == nullptr) spi = (SpritePositioningInfo*)ImageArena.Alloc(2 * sizeof(SpritePositioningInfo)); - for (int i = 0; i < 2; i++) - { - auto& spi = this->spi[i]; - spi.mSpriteU[0] = spi.mSpriteV[0] = 0.f; - spi.mSpriteU[1] = spi.mSpriteV[1] = 1.f; - spi.spriteWidth = GetTexelWidth(); - spi.spriteHeight = GetTexelHeight(); - - if (i == 1 && ShouldExpandSprite()) - { - spi.mTrimResult = Base->TrimBorders(spi.trim); // get the trim size before adding the empty frame - spi.spriteWidth += 2; - spi.spriteHeight += 2; - } - } - SetSpriteRect(); -} - -//=========================================================================== -// -// Set the sprite rectangle. This is separate because it may be called by a CVAR, too. -// -//=========================================================================== - -void FGameTexture::SetSpriteRect() -{ - - if (!spi) return; - auto leftOffset = GetTexelLeftOffset(r_spriteadjustHW); - auto topOffset = GetTexelTopOffset(r_spriteadjustHW); - - float fxScale = GetScaleX(); - float fyScale = GetScaleY(); - - for (int i = 0; i < 2; i++) - { - auto& spi = this->spi[i]; - - // mSpriteRect is for positioning the sprite in the scene. - spi.mSpriteRect.left = -leftOffset / fxScale; - spi.mSpriteRect.top = -topOffset / fyScale; - spi.mSpriteRect.width = spi.spriteWidth / fxScale; - spi.mSpriteRect.height = spi.spriteHeight / fyScale; - - if (i == 1 && ShouldExpandSprite()) - { - // a little adjustment to make sprites look better with texture filtering: - // create a 1 pixel wide empty frame around them. - - int oldwidth = spi.spriteWidth - 2; - int oldheight = spi.spriteHeight - 2; - - leftOffset += 1; - topOffset += 1; - - // Reposition the sprite with the frame considered - spi.mSpriteRect.left = -(float)leftOffset / fxScale; - spi.mSpriteRect.top = -(float)topOffset / fyScale; - spi.mSpriteRect.width = (float)spi.spriteWidth / fxScale; - spi.mSpriteRect.height = (float)spi.spriteHeight / fyScale; - - if (spi.mTrimResult > 0) - { - spi.mSpriteRect.left += (float)spi.trim[0] / fxScale; - spi.mSpriteRect.top += (float)spi.trim[1] / fyScale; - - spi.mSpriteRect.width -= float(oldwidth - spi.trim[2]) / fxScale; - spi.mSpriteRect.height -= float(oldheight - spi.trim[3]) / fyScale; - - spi.mSpriteU[0] = (float)spi.trim[0] / (float)spi.spriteWidth; - spi.mSpriteV[0] = (float)spi.trim[1] / (float)spi.spriteHeight; - spi.mSpriteU[1] -= float(oldwidth - spi.trim[0] - spi.trim[2]) / (float)spi.spriteWidth; - spi.mSpriteV[1] -= float(oldheight - spi.trim[1] - spi.trim[3]) / (float)spi.spriteHeight; - } - } - } -} - - -void FGameTexture::CleanHardwareData(bool full) -{ - -} - //=========================================================================== // // Create a hardware texture for this texture image. @@ -813,104 +527,6 @@ IHardwareTexture* FTexture::GetHardwareTexture(int translation, int scaleflags) return nullptr; } -//=========================================================================== -// -// Coordinate helper. -// The only reason this is even needed is that many years ago someone -// was convinced that having per-texel panning on walls was a good idea. -// If it wasn't for this relatively useless feature the entire positioning -// code for wall textures could be a lot simpler. -// -//=========================================================================== - -//=========================================================================== -// -// -// -//=========================================================================== - -float FTexCoordInfo::RowOffset(float rowoffset) const -{ - float scale = fabs(mScale.Y); - if (scale == 1.f || mWorldPanning) return rowoffset; - else return rowoffset / scale; -} - -//=========================================================================== -// -// -// -//=========================================================================== - -float FTexCoordInfo::TextureOffset(float textureoffset) const -{ - float scale = fabs(mScale.X); - if (scale == 1.f || mWorldPanning) return textureoffset; - else return textureoffset / scale; -} - -//=========================================================================== -// -// Returns the size for which texture offset coordinates are used. -// -//=========================================================================== - -float FTexCoordInfo::TextureAdjustWidth() const -{ - if (mWorldPanning) - { - float tscale = fabs(mTempScale.X); - if (tscale == 1.f) return (float)mRenderWidth; - else return mWidth / fabs(tscale); - } - else return (float)mWidth; -} - - -//=========================================================================== -// -// Retrieve texture coordinate info for per-wall scaling -// -//=========================================================================== - -void FTexCoordInfo::GetFromTexture(FGameTexture *tex, float x, float y, bool forceworldpanning) -{ - if (x == 1.f) - { - mRenderWidth = xs_RoundToInt(tex->GetDisplayWidth()); - mScale.X = tex->GetScaleX(); - mTempScale.X = 1.f; - } - else - { - float scale_x = x * tex->GetScaleX(); - mRenderWidth = xs_CeilToInt(tex->GetTexelWidth() / scale_x); - mScale.X = scale_x; - mTempScale.X = x; - } - - if (y == 1.f) - { - mRenderHeight = xs_RoundToInt(tex->GetDisplayHeight()); - mScale.Y = tex->GetScaleY(); - mTempScale.Y = 1.f; - } - else - { - float scale_y = y * tex->GetScaleY(); - mRenderHeight = xs_CeilToInt(tex->GetTexelHeight() / scale_y); - mScale.Y = scale_y; - mTempScale.Y = y; - } - if (tex->isHardwareCanvas()) - { - mScale.Y = -mScale.Y; - mRenderHeight = -mRenderHeight; - } - mWorldPanning = tex->useWorldPanning() || forceworldpanning; - mWidth = tex->GetTexelWidth(); -} - //========================================================================== // @@ -930,76 +546,3 @@ FWrapperTexture::FWrapperTexture(int w, int h, int bits) } -FGameTexture::FGameTexture(FTexture* wrap, const char* name) : Name(name) -{ - if (wrap) Setup(wrap); -} - -void FGameTexture::Setup(FTexture *wrap) -{ - Base = wrap; - id.SetInvalid(); - TexelWidth = Base->GetWidth(); - DisplayWidth = (float)TexelWidth; - TexelHeight = Base->GetHeight(); - DisplayHeight = (float)TexelHeight; - auto img = Base->GetImage(); - if (img) - { - auto ofs = img->GetOffsets(); - LeftOffset[0] = LeftOffset[1] = ofs.first; - TopOffset[0] = TopOffset[1] = ofs.second; - } - else - { - LeftOffset[0] = LeftOffset[1] = - TopOffset[0] = TopOffset[1] = 0; - - } - ScaleX = ScaleY = 1.f; -} - - -FGameTexture::~FGameTexture() -{ - FGameTexture* link = fileSystem.GetLinkedTexture(GetSourceLump()); - if (link == this) fileSystem.SetLinkedTexture(GetSourceLump(), nullptr); - if (SoftwareTexture != nullptr) - { - delete SoftwareTexture; - SoftwareTexture = nullptr; - } - for (auto &mat : Material) - { - if (mat != nullptr) DeleteMaterial(mat); - mat = nullptr; - } - -} - -bool FGameTexture::isUserContent() const -{ - int filenum = fileSystem.GetFileContainer(Base->GetSourceLump()); - return (filenum > fileSystem.GetMaxIwadNum()); -} - - -//----------------------------------------------------------------------------- -// -// Make sprite offset adjustment user-configurable per renderer. -// -//----------------------------------------------------------------------------- - -CUSTOM_CVAR(Int, r_spriteadjust, 2, CVAR_ARCHIVE | CVAR_GLOBALCONFIG) -{ - r_spriteadjustHW = !!(self & 2); - r_spriteadjustSW = !!(self & 1); - for (int i = 0; i < TexMan.NumTextures(); i++) - { - auto tex = TexMan.GetGameTexture(FSetTextureID(i)); - if (tex->GetTexelLeftOffset(0) != tex->GetTexelLeftOffset(1) || tex->GetTexelTopOffset(0) != tex->GetTexelTopOffset(1)) - { - tex->SetSpriteRect(); - } - } -} diff --git a/src/common/textures/texturemanager.cpp b/src/common/textures/texturemanager.cpp index 6d6a0dbcf..34483fc0c 100644 --- a/src/common/textures/texturemanager.cpp +++ b/src/common/textures/texturemanager.cpp @@ -126,6 +126,25 @@ void FTextureManager::FlushAll() } } +//========================================================================== +// +// Examines the lump contents to decide what type of texture to create, +// and creates the texture. +// +//========================================================================== + +static FTexture* CreateTextureFromLump(int lumpnum, bool allowflats = false) +{ + if (lumpnum == -1) return nullptr; + + auto image = FImageSource::GetImage(lumpnum, allowflats); + if (image != nullptr) + { + return new FImageTexture(image); + } + return nullptr; +} + //========================================================================== // // FTextureManager :: CheckForTexture @@ -230,7 +249,7 @@ FTextureID FTextureManager::CheckForTexture (const char *name, ETextureType uset if (tex == NO_TEXTURE) return FTextureID(-1); if (tex != NULL) return tex->GetID(); if (flags & TEXMAN_DontCreate) return FTextureID(-1); // we only want to check, there's no need to create a texture if we don't have one yet. - tex = MakeGameTexture(FTexture::CreateTexture(lump), nullptr, ETextureType::Override); + tex = MakeGameTexture(CreateTextureFromLump(lump), nullptr, ETextureType::Override); if (tex != NULL) { tex->AddAutoMaterials(); @@ -424,7 +443,7 @@ FTextureID FTextureManager::CreateTexture (int lumpnum, ETextureType usetype) { FString str; fileSystem.GetFileShortName(str, lumpnum); - auto out = MakeGameTexture(FTexture::CreateTexture(lumpnum, usetype == ETextureType::Flat), str, usetype); + auto out = MakeGameTexture(CreateTextureFromLump(lumpnum, usetype == ETextureType::Flat), str, usetype); if (out != NULL) { @@ -587,7 +606,7 @@ void FTextureManager::AddHiresTextures (int wadnum) if (amount == 0) { // A texture with this name does not yet exist - auto newtex = MakeGameTexture(FTexture::CreateTexture(firsttx), Name, ETextureType::Override); + auto newtex = MakeGameTexture(CreateTextureFromLump(firsttx), Name, ETextureType::Override); if (newtex != NULL) { AddGameTexture(newtex); @@ -597,7 +616,7 @@ void FTextureManager::AddHiresTextures (int wadnum) { for(unsigned int i = 0; i < tlist.Size(); i++) { - FTexture * newtex = FTexture::CreateTexture (firsttx); + FTexture * newtex = CreateTextureFromLump(firsttx); if (newtex != NULL) { auto oldtex = Textures[tlist[i].GetIndex()].Texture; @@ -695,7 +714,7 @@ void FTextureManager::ParseTextureDef(int lump, FMultipatchTextureBuilder &build (sl=oldtex->GetSourceLump()) >= 0 && fileSystem.GetFileNamespace(sl) == ns_sprites) ) { - FTexture * newtex = FTexture::CreateTexture (lumpnum); + FTexture * newtex = CreateTextureFromLump(lumpnum); if (newtex != NULL) { // Replace the entire texture and adjust the scaling and offset factors. @@ -733,7 +752,7 @@ void FTextureManager::ParseTextureDef(int lump, FMultipatchTextureBuilder &build if (lumpnum>=0) { - auto newtex = MakeGameTexture(FTexture::CreateTexture(lumpnum), src, ETextureType::Override); + auto newtex = MakeGameTexture(CreateTextureFromLump(lumpnum), src, ETextureType::Override); if (newtex != NULL) { @@ -953,7 +972,7 @@ void FTextureManager::AddTexturesForWad(int wadnum, FMultipatchTextureBuilder &b // Try to create a texture from this lump and add it. // Unfortunately we have to look at everything that comes through here... - auto out = MakeGameTexture(FTexture::CreateTexture(i), Name, skin ? ETextureType::SkinGraphic : ETextureType::MiscPatch); + auto out = MakeGameTexture(CreateTextureFromLump(i), Name, skin ? ETextureType::SkinGraphic : ETextureType::MiscPatch); if (out != NULL) { diff --git a/src/common/textures/textures.h b/src/common/textures/textures.h index d4e801b8f..1cb4843fb 100644 --- a/src/common/textures/textures.h +++ b/src/common/textures/textures.h @@ -42,17 +42,26 @@ #include "textureid.h" #include #include "hw_texcontainer.h" +#include "floatrect.h" #include "refcounted.h" -#include "xs_Float.h" - -// 15 because 0th texture is our texture -#define MAX_CUSTOM_HW_SHADER_TEXTURES 15 typedef TMap SpriteHits; class FImageSource; class FGameTexture; class IHardwareTexture; + +enum +{ + CLAMP_NONE = 0, + CLAMP_X = 1, + CLAMP_Y = 2, + CLAMP_XY = 3, + CLAMP_XY_NOMIP = 4, + CLAMP_NOFILTER = 5, + CLAMP_CAMTEX = 6, +}; + enum MaterialShaderIndex { SHADER_Default, @@ -101,27 +110,6 @@ struct UserShaderDesc extern TArray usershaders; - -struct FloatRect -{ - float left,top; - float width,height; - - - void Offset(float xofs,float yofs) - { - left+=xofs; - top+=yofs; - } - void Scale(float xfac,float yfac) - { - left*=xfac; - width*=xfac; - top*=yfac; - height*=yfac; - } -}; - class FBitmap; struct FRemapTable; struct FCopyInfo; @@ -216,26 +204,6 @@ struct FTextureBuffer }; -struct SpritePositioningInfo -{ - uint16_t trim[4]; - int spriteWidth, spriteHeight; - float mSpriteU[2], mSpriteV[2]; - FloatRect mSpriteRect; - uint8_t mTrimResult; - - float GetSpriteUL() const { return mSpriteU[0]; } - float GetSpriteVT() const { return mSpriteV[0]; } - float GetSpriteUR() const { return mSpriteU[1]; } - float GetSpriteVB() const { return mSpriteV[1]; } - - const FloatRect &GetSpriteRect() const - { - return mSpriteRect; - } - -}; - // Base texture class class FTexture : public RefCountedBase { @@ -256,7 +224,6 @@ protected: public: IHardwareTexture* GetHardwareTexture(int translation, int scaleflags); - static FTexture *CreateTexture(int lumpnum, bool allowflats = false); virtual FImageSource *GetImage() const { return nullptr; } void CreateUpsampledTextureBuffer(FTextureBuffer &texbuffer, bool hasAlpha, bool checkonly); void CleanHardwareTextures(); @@ -387,299 +354,8 @@ public: }; -struct MaterialLayers -{ - float Glossiness; - float SpecularLevel; - FGameTexture* Brightmap; - FGameTexture* Normal; - FGameTexture* Specular; - FGameTexture* Metallic; - FGameTexture* Roughness; - FGameTexture* AmbientOcclusion; - FGameTexture* CustomShaderTextures[MAX_CUSTOM_HW_SHADER_TEXTURES]; -}; - -struct FTexCoordInfo -{ - int mRenderWidth; - int mRenderHeight; - int mWidth; - FVector2 mScale; - FVector2 mTempScale; - bool mWorldPanning; - - float FloatToTexU(float v) const { return v / mRenderWidth; } - float FloatToTexV(float v) const { return v / mRenderHeight; } - float RowOffset(float ofs) const; - float TextureOffset(float ofs) const; - float TextureAdjustWidth() const; - void GetFromTexture(FGameTexture* tex, float x, float y, bool forceworldpanning); -}; - -enum -{ - CLAMP_NONE = 0, - CLAMP_X = 1, - CLAMP_Y = 2, - CLAMP_XY = 3, - CLAMP_XY_NOMIP = 4, - CLAMP_NOFILTER = 5, - CLAMP_CAMTEX = 6, -}; - - -enum EGameTexFlags -{ - GTexf_NoDecals = 1, // Decals should not stick to texture - GTexf_WorldPanning = 2, // Texture is panned in world units rather than texels - GTexf_FullNameTexture = 4, // Name is taken from the file system. - GTexf_Glowing = 8, // Texture emits a glow - GTexf_AutoGlowing = 16, // Glow info is determined from texture image. - GTexf_RenderFullbright = 32, // always draw fullbright - GTexf_DisableFullbrightSprites = 64, // This texture will not be displayed as fullbright sprite - GTexf_BrightmapChecked = 128, // Check for a colormap-based brightmap was already done. -}; - -// Refactoring helper to allow piece by piece adjustment of the API -class FGameTexture -{ - friend class FMaterial; - - // Material layers. These are shared so reference counting is used. - RefCountedPtr Base; - RefCountedPtr Brightmap; - RefCountedPtr Detailmap; - RefCountedPtr Glowmap; - RefCountedPtr Normal; // Normal map texture - RefCountedPtr Specular; // Specular light texture for the diffuse+normal+specular light model - RefCountedPtr Metallic; // Metalness texture for the physically based rendering (PBR) light model - RefCountedPtr Roughness; // Roughness texture for PBR - RefCountedPtr AmbientOcclusion; // Ambient occlusion texture for PBR - RefCountedPtr CustomShaderTextures[MAX_CUSTOM_HW_SHADER_TEXTURES]; // Custom texture maps for custom hardware shaders - - FString Name; - FTextureID id; - - uint16_t TexelWidth, TexelHeight; - int16_t LeftOffset[2], TopOffset[2]; - float DisplayWidth, DisplayHeight; - float ScaleX, ScaleY; - - int8_t shouldUpscaleFlag = 0; // Without explicit setup, scaling is disabled for a texture. - ETextureType UseType = ETextureType::Wall; // This texture's primary purpose - SpritePositioningInfo* spi = nullptr; - - ISoftwareTexture* SoftwareTexture = nullptr; - FMaterial* Material[4] = { }; - - // Material properties - float Glossiness = 10.f; - float SpecularLevel = 0.1f; - float shaderspeed = 1.f; - int shaderindex = 0; - - int flags = 0; - uint8_t warped = 0, expandSprite = -1; - uint16_t GlowHeight; - PalEntry GlowColor = 0; - - int16_t SkyOffset = 0; - uint16_t Rotations = 0xffff; - - -public: - FGameTexture(FTexture* wrap, const char *name); - ~FGameTexture(); - void Setup(FTexture* wrap); - FTextureID GetID() const { return id; } - void SetID(FTextureID newid) { id = newid; } // should only be called by the texture manager - const FString& GetName() const { return Name; } - void SetName(const char* name) { Name = name; } // should only be called by setup code. - - float GetScaleX() { return ScaleX; } - float GetScaleY() { return ScaleY; } - float GetDisplayWidth() const { return DisplayWidth; } - float GetDisplayHeight() const { return DisplayHeight; } - int GetTexelWidth() const { return TexelWidth; } - int GetTexelHeight() const { return TexelHeight; } - - void CreateDefaultBrightmap(); - void AddAutoMaterials(); - bool ShouldExpandSprite(); - void SetupSpriteData(); - void SetSpriteRect(); - - ETextureType GetUseType() const { return UseType; } - void SetUpscaleFlag(int what) { shouldUpscaleFlag = what; } - int GetUpscaleFlag() { return shouldUpscaleFlag; } - - FTexture* GetTexture() { return Base.get(); } - int GetSourceLump() const { return Base->GetSourceLump(); } - void SetBrightmap(FGameTexture* tex) { Brightmap = tex->GetTexture(); } - - int GetTexelLeftOffset(int adjusted = 0) const { return LeftOffset[adjusted]; } - int GetTexelTopOffset(int adjusted = 0) const { return TopOffset[adjusted]; } - float GetDisplayLeftOffset(int adjusted = 0) const { return LeftOffset[adjusted] / ScaleX; } - float GetDisplayTopOffset(int adjusted = 0) const { return TopOffset[adjusted] / ScaleY; } - - bool isMiscPatch() const { return GetUseType() == ETextureType::MiscPatch; } // only used by the intermission screen to decide whether to tile the background image or not. - bool isFullbrightDisabled() const { return !!(flags & GTexf_DisableFullbrightSprites); } - bool isFullbright() const { return !!(flags & GTexf_RenderFullbright); } - bool isFullNameTexture() const { return !!(flags & GTexf_FullNameTexture); } - bool expandSprites() const { return !!expandSprite; } - bool useWorldPanning() const { return !!(flags & GTexf_WorldPanning); } - void SetWorldPanning(bool on) { if (on) flags |= GTexf_WorldPanning; else flags &= ~GTexf_WorldPanning; } - bool allowNoDecals() const { return !!(flags & GTexf_NoDecals); } - void SetNoDecals(bool on) { if (on) flags |= GTexf_NoDecals; else flags &= ~GTexf_NoDecals; } - - bool isValid() const { return UseType != ETextureType::Null; } - int isWarped() { return warped; } - void SetWarpStyle(int style) { warped = style; } - bool isMasked() { return Base->Masked; } - bool isHardwareCanvas() const { return Base->isHardwareCanvas(); } // There's two here so that this can deal with software canvases in the hardware renderer later. - bool isSoftwareCanvas() const { return Base->isCanvas(); } - - void SetTranslucent(bool on) { Base->bTranslucent = on; } - void SetUseType(ETextureType type) { UseType = type; } - int GetRotations() const { return Rotations; } - void SetRotations(int rot) { Rotations = int16_t(rot); } - void SetSkyOffset(int offs) { SkyOffset = offs; } - int GetSkyOffset() const { return SkyOffset; } - - ISoftwareTexture* GetSoftwareTexture() - { - return SoftwareTexture; - } - void SetSoftwareTexture(ISoftwareTexture* swtex) - { - SoftwareTexture = swtex; - } - - FMaterial* GetMaterial(int num) - { - return Material[num]; - } - - int GetShaderIndex() const { return shaderindex; } - float GetShaderSpeed() const { return shaderspeed; } - void SetShaderSpeed(float speed) { shaderspeed = speed; } - void SetShaderIndex(int index) { shaderindex = index; } - void SetShaderLayers(MaterialLayers& lay) - { - // Only update layers that have something defind. - if (lay.Glossiness > -1000) Glossiness = lay.Glossiness; - if (lay.SpecularLevel > -1000) SpecularLevel = lay.SpecularLevel; - if (lay.Brightmap) Brightmap = lay.Brightmap->GetTexture(); - if (lay.Normal) Normal = lay.Normal->GetTexture(); - if (lay.Specular) Specular = lay.Specular->GetTexture(); - if (lay.Metallic) Metallic = lay.Metallic->GetTexture(); - if (lay.Roughness) Roughness = lay.Roughness->GetTexture(); - if (lay.AmbientOcclusion) AmbientOcclusion = lay.AmbientOcclusion->GetTexture(); - for (int i = 0; i < MAX_CUSTOM_HW_SHADER_TEXTURES; i++) - { - if (lay.CustomShaderTextures[i]) CustomShaderTextures[i] = lay.CustomShaderTextures[i]->GetTexture(); - } - } - float GetGlossiness() const { return Glossiness; } - float GetSpecularLevel() const { return SpecularLevel; } - - void CopySize(FGameTexture* BaseTexture) - { - Base->CopySize(BaseTexture->Base.get()); - } - - // Glowing is a pure material property that should not filter down to the actual texture objects. - void GetGlowColor(float* data); - bool isGlowing() const { return !!(flags & GTexf_Glowing); } - bool isAutoGlowing() const { return !!(flags & GTexf_AutoGlowing); } - int GetGlowHeight() const { return GlowHeight; } - void SetAutoGlowing() { flags |= (GTexf_AutoGlowing | GTexf_Glowing | GTexf_RenderFullbright); } - void SetGlowHeight(int v) { GlowHeight = v; } - void SetFullbright() { flags |= GTexf_RenderFullbright; } - void SetDisableFullbright(bool on) { if (on) flags |= GTexf_DisableFullbrightSprites; else flags &= ~GTexf_DisableFullbrightSprites; } - void SetGlowing(PalEntry color) { flags = (flags & ~GTexf_AutoGlowing) | GTexf_Glowing; GlowColor = color; } - - bool isUserContent() const; - int CheckRealHeight() { return xs_RoundToInt(Base->CheckRealHeight() / ScaleY); } - void SetSize(int x, int y) - { - TexelWidth = x; - TexelHeight = y; - SetDisplaySize(float(x), float(y)); - } - void SetDisplaySize(float w, float h) - { - DisplayWidth = w; - DisplayHeight = h; - ScaleX = TexelWidth / w; - ScaleY = TexelHeight / h; - - // compensate for roundoff errors - if (int(ScaleX * w) != TexelWidth) ScaleX += (1 / 65536.); - if (int(ScaleY * h) != TexelHeight) ScaleY += (1 / 65536.); - - } - void SetOffsets(int which, int x, int y) - { - LeftOffset[which] = x; - TopOffset[which] = y; - } - void SetScale(float x, float y) - { - ScaleX = x; - ScaleY = y; - DisplayWidth = x * TexelWidth; - DisplayHeight = y * TexelHeight; - } - - const SpritePositioningInfo& GetSpritePositioning(int which) { if (spi == nullptr) SetupSpriteData(); return spi[which]; } - int GetAreas(FloatRect** pAreas) const; - - bool GetTranslucency() - { - return Base->GetTranslucency(); - } - - int GetClampMode(int clampmode) - { - if (GetUseType() == ETextureType::SWCanvas) clampmode = CLAMP_NOFILTER; - else if (isHardwareCanvas()) clampmode = CLAMP_CAMTEX; - else if ((isWarped() || shaderindex >= FIRST_USER_SHADER) && clampmode <= CLAMP_XY) clampmode = CLAMP_NONE; - return clampmode; - } - - void CleanHardwareData(bool full = true); - -}; - -inline FGameTexture* MakeGameTexture(FTexture* tex, const char *name, ETextureType useType) -{ - if (!tex) return nullptr; - auto t = new FGameTexture(tex, name); - t->SetUseType(useType); - return t; -} - -enum EUpscaleFlags -{ - UF_None = 0, - UF_Texture = 1, - UF_Sprite = 2, - UF_Font = 4 -}; - -extern int upscalemask; -void UpdateUpscaleMask(); - -int calcShouldUpscale(FGameTexture* tex); -inline int shouldUpscale(FGameTexture* tex, EUpscaleFlags UseType) -{ - // This only checks the global scale mask and the texture's validation for upscaling. Everything else has been done up front elsewhere. - if (!(upscalemask & UseType)) return 0; - return tex->GetUpscaleFlag(); -} +#include "gametexture.h" #endif diff --git a/src/common/utility/floatrect.h b/src/common/utility/floatrect.h new file mode 100644 index 000000000..8274b8498 --- /dev/null +++ b/src/common/utility/floatrect.h @@ -0,0 +1,21 @@ +#pragma once + +struct FloatRect +{ + float left,top; + float width,height; + + + void Offset(float xofs,float yofs) + { + left+=xofs; + top+=yofs; + } + void Scale(float xfac,float yfac) + { + left*=xfac; + width*=xfac; + top*=yfac; + height*=yfac; + } +}; diff --git a/src/r_data/gldefs.cpp b/src/r_data/gldefs.cpp index 55c6b6050..db9af53cf 100644 --- a/src/r_data/gldefs.cpp +++ b/src/r_data/gldefs.cpp @@ -1276,7 +1276,7 @@ class GLDefsParser if (tex) { bool okay = false; - for (int i = 0; i < MAX_CUSTOM_HW_SHADER_TEXTURES; i++) + for (size_t i = 0; i < countof(mlay.CustomShaderTextures); i++) { if (!mlay.CustomShaderTextures[i]) { @@ -1287,7 +1287,7 @@ class GLDefsParser } texNameList.Push(textureName); - texNameIndex.Push(i); + texNameIndex.Push((int)i); okay = true; break; } @@ -1571,7 +1571,7 @@ class GLDefsParser } sc.MustGetString(); bool okay = false; - for (int i = 0; i < MAX_CUSTOM_HW_SHADER_TEXTURES; i++) + for (size_t i = 0; i < countof(mlay.CustomShaderTextures); i++) { if (!mlay.CustomShaderTextures[i]) { @@ -1582,7 +1582,7 @@ class GLDefsParser } texNameList.Push(textureName); - texNameIndex.Push(i); + texNameIndex.Push((int)i); okay = true; break; } diff --git a/src/rendering/hwrenderer/textures/hw_precache.cpp b/src/rendering/hwrenderer/textures/hw_precache.cpp index 55f92a8d4..908fe66d9 100644 --- a/src/rendering/hwrenderer/textures/hw_precache.cpp +++ b/src/rendering/hwrenderer/textures/hw_precache.cpp @@ -227,11 +227,14 @@ void hw_PrecacheTexture(uint8_t *texhitlist, TMap &actorhitl auto tex = TexMan.GameByIndex(i); if (tex != nullptr && tex->GetUseType() != ETextureType::FontChar) { - // For now, only delete what's in neither list. The logic being used here does not really work that well for selective deletion. + // This is rather counterproductive in a system that can share hardware textures between game textures. + // The precacher needs to be redone to account for that. For now, just skip the deletion, for any normal sized map this won't be a problem. +#if 0 if (usedTextures.CheckKey(tex->GetTexture()) == nullptr && usedSprites.CheckKey(tex->GetTexture()) == nullptr) { tex->CleanHardwareData(); } +#endif } }