/* ** gl_material.cpp ** **--------------------------------------------------------------------------- ** Copyright 2004-2009 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. ** 4. When not used as part of GZDoom or a GZDoom derivative, this code will be ** covered by the terms of the GNU Lesser General Public License as published ** by the Free Software Foundation; either version 2.1 of the License, or (at ** your option) any later version. ** ** 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 "gl/system/gl_system.h" #include "w_wad.h" #include "m_png.h" #include "sbar.h" #include "gi.h" #include "cmdlib.h" #include "c_dispatch.h" #include "stats.h" #include "r_utility.h" #include "templates.h" #include "sc_man.h" #include "colormatcher.h" //#include "gl/gl_intern.h" #include "gl/system/gl_interface.h" #include "gl/system/gl_framebuffer.h" #include "gl/renderer/gl_lightdata.h" #include "gl/data/gl_data.h" #include "gl/textures/gl_texture.h" #include "gl/textures/gl_translate.h" #include "gl/textures/gl_bitmap.h" #include "gl/textures/gl_material.h" #include "gl/shaders/gl_shader.h" EXTERN_CVAR(Bool, gl_render_precise) EXTERN_CVAR(Int, gl_lightmode) EXTERN_CVAR(Bool, gl_precache) EXTERN_CVAR(Bool, gl_texture_usehires) //=========================================================================== // // The GL texture maintenance class // //=========================================================================== //=========================================================================== // // Constructor // //=========================================================================== FGLTexture::FGLTexture(FTexture * tx, bool expandpatches) { assert(tx->gl_info.SystemTexture == NULL); tex = tx; glpatch=NULL; for(int i=0;i<5;i++) gltexture[i]=NULL; HiresLump=-1; hirestexture = NULL; currentwarp = 0; bHasColorkey = false; bIsTransparent = -1; bExpand = expandpatches; tex->gl_info.SystemTexture = this; } //=========================================================================== // // Destructor // //=========================================================================== FGLTexture::~FGLTexture() { Clean(true); if (hirestexture) delete hirestexture; } //========================================================================== // // Checks for the presence of a hires texture replacement and loads it // //========================================================================== unsigned char *FGLTexture::LoadHiresTexture(FTexture *tex, int *width, int *height, int cm) { if (HiresLump==-1) { bHasColorkey = false; HiresLump = CheckDDPK3(tex); if (HiresLump < 0) HiresLump = CheckExternalFile(tex, bHasColorkey); if (HiresLump >=0) { hirestexture = FTexture::CreateTexture(HiresLump, FTexture::TEX_Any); } } if (hirestexture != NULL) { int w=hirestexture->GetWidth(); int h=hirestexture->GetHeight(); unsigned char * buffer=new unsigned char[w*(h+1)*4]; memset(buffer, 0, w * (h+1) * 4); FGLBitmap bmp(buffer, w*4, w, h); bmp.SetTranslationInfo(cm); int trans = hirestexture->CopyTrueColorPixels(&bmp, 0, 0); hirestexture->CheckTrans(buffer, w*h, trans); bIsTransparent = hirestexture->gl_info.mIsTransparent; if (bHasColorkey) { // This is a crappy Doomsday color keyed image // We have to remove the key manually. :( DWORD * dwdata=(DWORD*)buffer; for (int i=(w*h);i>0;i--) { if (dwdata[i]==0xffffff00 || dwdata[i]==0xffff00ff) dwdata[i]=0; } } *width = w; *height = h; return buffer; } return NULL; } //=========================================================================== // // Deletes all allocated resources // //=========================================================================== void FGLTexture::Clean(bool all) { for(int i=0;i<5;i++) { if (gltexture[i]) { if (!all) gltexture[i]->Clean(false); else { delete gltexture[i]; gltexture[i]=NULL; } } } if (glpatch) { if (!all) glpatch->Clean(false); else { delete glpatch; glpatch=NULL; } } } //=========================================================================== // // FGLTex::WarpBuffer // //=========================================================================== BYTE *FGLTexture::WarpBuffer(BYTE *buffer, int Width, int Height, int warp) { if (Width > 256 || Height > 256) return buffer; DWORD *in = (DWORD*)buffer; DWORD *out = (DWORD*)new BYTE[4*Width*Height]; float Speed = static_cast(tex)->GetSpeed(); static_cast(tex)->GenTime = r_FrameTime; static DWORD linebuffer[256]; // anything larger will bring down performance so it is excluded above. DWORD timebase = DWORD(r_FrameTime*Speed*23/28); int xsize = Width; int ysize = Height; int xmask = xsize - 1; int ymask = ysize - 1; int ds_xbits; int i,x; if (warp == 1) { for(ds_xbits=-1,i=Width; i; i>>=1, ds_xbits++); for (x = xsize-1; x >= 0; x--) { int yt, yf = (finesine[(timebase+(x+17)*128)&FINEMASK]>>13) & ymask; const DWORD *source = in + x; DWORD *dest = out + x; for (yt = ysize; yt; yt--, yf = (yf+1)&ymask, dest += xsize) { *dest = *(source+(yf<= 0; y--) { int xt, xf = (finesine[(timebase+y*128)&FINEMASK]>>13) & xmask; DWORD *source = out + (y<>=1, ybits++); DWORD timebase = (r_FrameTime * Speed * 40 / 28); for (x = xsize-1; x >= 0; x--) { for (int y = ysize-1; y >= 0; y--) { int xt = (x + 128 + ((finesine[(y*128 + timebase*5 + 900) & 8191]*2)>>FRACBITS) + ((finesine[(x*256 + timebase*4 + 300) & 8191]*2)>>FRACBITS)) & xmask; int yt = (y + 128 + ((finesine[(y*128 + timebase*3 + 700) & 8191]*2)>>FRACBITS) + ((finesine[(x*256 + timebase*4 + 1200) & 8191]*2)>>FRACBITS)) & ymask; const DWORD *source = in + (xt << ybits) + yt; DWORD *dest = out + (x << ybits) + y; *dest = *source; } } } delete [] buffer; return (BYTE*)out; } //=========================================================================== // // Initializes the buffer for the texture data // //=========================================================================== unsigned char * FGLTexture::CreateTexBuffer(int cm, int translation, int & w, int & h, bool expand, FTexture *hirescheck, int warp) { unsigned char * buffer; int W, H; // Textures that are already scaled in the texture lump will not get replaced // by hires textures if (gl_texture_usehires && hirescheck != NULL) { buffer = LoadHiresTexture (hirescheck, &w, &h, cm); if (buffer) { return buffer; } } W = w = tex->GetWidth() + expand*2; H = h = tex->GetHeight() + expand*2; buffer=new unsigned char[W*(H+1)*4]; memset(buffer, 0, W * (H+1) * 4); FGLBitmap bmp(buffer, W*4, W, H); bmp.SetTranslationInfo(cm, translation); if (tex->bComplex) { FBitmap imgCreate; // The texture contains special processing so it must be composited using the // base bitmap class and then be converted as a whole. if (imgCreate.Create(W, H)) { memset(imgCreate.GetPixels(), 0, W * H * 4); int trans = tex->CopyTrueColorPixels(&imgCreate, expand, expand); bmp.CopyPixelDataRGB(0, 0, imgCreate.GetPixels(), W, H, 4, W * 4, 0, CF_BGRA); tex->CheckTrans(buffer, W*H, trans); bIsTransparent = tex->gl_info.mIsTransparent; } } else if (translation<=0) { int trans = tex->CopyTrueColorPixels(&bmp, expand, expand); tex->CheckTrans(buffer, W*H, trans); bIsTransparent = tex->gl_info.mIsTransparent; } else { // When using translations everything must be mapped to the base palette. // Since FTexture's method is doing exactly that by calling GetPixels let's use that here // to do all the dirty work for us. ;) tex->FTexture::CopyTrueColorPixels(&bmp, expand, expand); bIsTransparent = 0; } if (warp != 0) { buffer = WarpBuffer(buffer, W, H, warp); } // [BB] The hqnx upsampling (not the scaleN one) destroys partial transparency, don't upsamle textures using it. // Also don't upsample warped textures. else //if (bIsTransparent != 1) { // [BB] Potentially upsample the buffer. buffer = gl_CreateUpsampledTextureBuffer ( tex, buffer, W, H, w, h, bIsTransparent || cm == CM_SHADE ); } currentwarp = warp; currentwarptime = gl_frameMS; return buffer; } //=========================================================================== // // Create hardware texture for world use // //=========================================================================== FHardwareTexture *FGLTexture::CreateTexture(int clampmode) { if (tex->UseType==FTexture::TEX_Null) return NULL; // Cannot register a NULL texture if (!gltexture[clampmode]) { gltexture[clampmode] = new FHardwareTexture(tex->GetWidth(), tex->GetHeight(), true, true, false, tex->gl_info.bNoCompress); } return gltexture[clampmode]; } //=========================================================================== // // Create Hardware texture for patch use // //=========================================================================== bool FGLTexture::CreatePatch() { if (tex->UseType==FTexture::TEX_Null) return false; // Cannot register a NULL texture if (!glpatch) { glpatch=new FHardwareTexture(tex->GetWidth() + bExpand, tex->GetHeight() + bExpand, false, false, tex->gl_info.bNoFilter, tex->gl_info.bNoCompress); } if (glpatch) return true; return false; } //=========================================================================== // // Binds a texture to the renderer // //=========================================================================== const FHardwareTexture *FGLTexture::Bind(int texunit, int cm, int clampmode, int translation, FTexture *hirescheck, int warp) { int usebright = false; if (translation <= 0) translation = -translation; else translation = GLTranslationPalette::GetInternalTranslation(translation); FHardwareTexture *hwtex; if (gltexture[4] != NULL && clampmode < 4 && gltexture[clampmode] == NULL) { hwtex = gltexture[clampmode] = gltexture[4]; gltexture[4] = NULL; if (hwtex->Bind(texunit, cm, translation)) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, (clampmode & GLT_CLAMPX)? GL_CLAMP_TO_EDGE : GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, (clampmode & GLT_CLAMPY)? GL_CLAMP_TO_EDGE : GL_REPEAT); } } else { hwtex = CreateTexture(clampmode); } if (hwtex) { if ((warp != 0 || currentwarp != warp) && currentwarptime != gl_frameMS) { // must recreate the texture Clean(true); hwtex = CreateTexture(clampmode); } // Texture has become invalid else if ((warp == 0 && !tex->bHasCanvas) && tex->CheckModified()) { Clean(true); hwtex = CreateTexture(clampmode); } // Bind it to the system. if (!hwtex->Bind(texunit, cm, translation)) { int w=0, h=0; // Create this texture unsigned char * buffer = NULL; if (!tex->bHasCanvas) { buffer = CreateTexBuffer(cm, translation, w, h, false, hirescheck, warp); tex->ProcessData(buffer, w, h, false); } if (!hwtex->CreateTexture(buffer, w, h, true, texunit, cm, translation)) { // could not create texture delete[] buffer; return NULL; } glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, (clampmode & GLT_CLAMPX)? GL_CLAMP_TO_EDGE : GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, (clampmode & GLT_CLAMPY)? GL_CLAMP_TO_EDGE : GL_REPEAT); delete[] buffer; } if (tex->bHasCanvas) static_cast(tex)->NeedUpdate(); return hwtex; } return NULL; } //=========================================================================== // // Binds a sprite to the renderer // //=========================================================================== const FHardwareTexture * FGLTexture::BindPatch(int texunit, int cm, int translation, int warp) { bool usebright = false; int transparm = translation; if (translation <= 0) translation = -translation; else translation = GLTranslationPalette::GetInternalTranslation(translation); if (CreatePatch()) { if (warp != 0 || currentwarp != warp) { // must recreate the texture Clean(true); CreatePatch(); } // Texture has become invalid else if ((warp == 0 && !tex->bHasCanvas) && tex->CheckModified()) { Clean(true); CreatePatch(); } // Bind it to the system. if (!glpatch->Bind(texunit, cm, translation)) { int w, h; // Create this texture unsigned char * buffer = CreateTexBuffer(cm, translation, w, h, bExpand, NULL, warp); tex->ProcessData(buffer, w, h, true); if (!glpatch->CreateTexture(buffer, w, h, false, texunit, cm, translation)) { // could not create texture delete[] buffer; return NULL; } delete[] buffer; glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); } return glpatch; } return NULL; } //=========================================================================== // // // //=========================================================================== fixed_t FTexCoordInfo::RowOffset(fixed_t rowoffset) const { if (mTempScaleY == FRACUNIT) { if (mScaleY==FRACUNIT || mWorldPanning) return rowoffset; else return FixedDiv(rowoffset, mScaleY); } else { if (mWorldPanning) return FixedDiv(rowoffset, mTempScaleY); else return FixedDiv(rowoffset, mScaleY); } } //=========================================================================== // // // //=========================================================================== fixed_t FTexCoordInfo::TextureOffset(fixed_t textureoffset) const { if (mTempScaleX == FRACUNIT) { if (mScaleX==FRACUNIT || mWorldPanning) return textureoffset; else return FixedDiv(textureoffset, mScaleX); } else { if (mWorldPanning) return FixedDiv(textureoffset, mTempScaleX); else return FixedDiv(textureoffset, mScaleX); } } //=========================================================================== // // Returns the size for which texture offset coordinates are used. // //=========================================================================== fixed_t FTexCoordInfo::TextureAdjustWidth() const { if (mWorldPanning) { if (mTempScaleX == FRACUNIT) return mRenderWidth; else return FixedDiv(mWidth, mTempScaleX); } else return mWidth; } //=========================================================================== // // // //=========================================================================== FGLTexture * FMaterial::ValidateSysTexture(FTexture * tex, bool expand) { if (tex && tex->UseType!=FTexture::TEX_Null) { FGLTexture *gltex = tex->gl_info.SystemTexture; if (gltex == NULL) { gltex = new FGLTexture(tex, expand); } return gltex; } return NULL; } //=========================================================================== // // Constructor // //=========================================================================== TArray FMaterial::mMaterials; int FMaterial::mMaxBound; FMaterial::FMaterial(FTexture * tx, bool forceexpand) { assert(tx->gl_info.Material == NULL); bool expanded = tx->UseType == FTexture::TEX_Sprite || tx->UseType == FTexture::TEX_SkinSprite || tx->UseType == FTexture::TEX_Decal || forceexpand; mShaderIndex = 0; // TODO: apply custom shader object here /* if (tx->CustomShaderDefinition) { } else */ if (tx->bWarped) { mShaderIndex = tx->bWarped; expanded = false; tx->gl_info.shaderspeed = static_cast(tx)->GetSpeed(); } else if (tx->bHasCanvas) { expanded = false; } else if (gl.shadermodel > 2) { if (tx->gl_info.shaderindex >= FIRST_USER_SHADER) { mShaderIndex = tx->gl_info.shaderindex; expanded = false; } else { tx->CreateDefaultBrightmap(); if (tx->gl_info.Brightmap != NULL) { ValidateSysTexture(tx->gl_info.Brightmap, expanded); FTextureLayer layer = {tx->gl_info.Brightmap, false}; mTextureLayers.Push(layer); mShaderIndex = 3; } } } for (int i=GLUSE_PATCH; i<=GLUSE_TEXTURE; i++) { Width[i] = tx->GetWidth(); Height[i] = tx->GetHeight(); LeftOffset[i] = tx->LeftOffset; TopOffset[i] = tx->TopOffset; RenderWidth[i] = tx->GetScaledWidth(); RenderHeight[i] = tx->GetScaledHeight(); } Width[GLUSE_SPRITE] = Width[GLUSE_PATCH]; Height[GLUSE_SPRITE] = Height[GLUSE_PATCH]; LeftOffset[GLUSE_SPRITE] = LeftOffset[GLUSE_PATCH]; TopOffset[GLUSE_SPRITE] = TopOffset[GLUSE_PATCH]; SpriteU[0] = SpriteV[0] = 0; spriteright = SpriteU[1] = Width[GLUSE_PATCH] / (float)FHardwareTexture::GetTexDimension(Width[GLUSE_PATCH]); spritebottom = SpriteV[1] = Height[GLUSE_PATCH] / (float)FHardwareTexture::GetTexDimension(Height[GLUSE_PATCH]); mTextureLayers.ShrinkToFit(); mMaxBound = -1; mMaterials.Push(this); tx->gl_info.Material = this; if (tx->bHasCanvas) tx->gl_info.mIsTransparent = 0; tex = tx; tx->gl_info.mExpanded = expanded; FTexture *basetex = tx->GetRedirect(gl.shadermodel < 4); if (!expanded && !basetex->gl_info.mExpanded && basetex->UseType != FTexture::TEX_Sprite) { // check if the texture is just a simple redirect to a patch // If so we should use the patch for texture creation to // avoid eventual redundancies. // This may only be done if both textures use the same expansion mode // Redirects to sprites are not permitted because sprites get expanded, however, this won't have been set // if the sprite hadn't been used yet. mBaseLayer = ValidateSysTexture(basetex, false); } else if (!expanded) { // if we got a non-expanded texture that redirects to an expanded one mBaseLayer = ValidateSysTexture(tx, false); } else { // a little adjustment to make sprites look better with texture filtering: // create a 1 pixel wide empty frame around them. RenderWidth[GLUSE_PATCH]+=2; RenderHeight[GLUSE_PATCH]+=2; Width[GLUSE_PATCH]+=2; Height[GLUSE_PATCH]+=2; LeftOffset[GLUSE_PATCH]+=1; TopOffset[GLUSE_PATCH]+=1; Width[GLUSE_SPRITE] += 2; Height[GLUSE_SPRITE] += 2; LeftOffset[GLUSE_SPRITE] += 1; TopOffset[GLUSE_SPRITE] += 1; spriteright = SpriteU[1] = Width[GLUSE_PATCH] / (float)FHardwareTexture::GetTexDimension(Width[GLUSE_PATCH]); spritebottom = SpriteV[1] = Height[GLUSE_PATCH] / (float)FHardwareTexture::GetTexDimension(Height[GLUSE_PATCH]); mBaseLayer = ValidateSysTexture(tx, true); if (gl.flags & RFL_NPOT_TEXTURE) // trimming only works if non-power-of-2 textures are supported { int trim[4]; if (TrimBorders(trim)) { Width[GLUSE_SPRITE] = trim[2] + 2; Height[GLUSE_SPRITE] = trim[3] + 2; LeftOffset[GLUSE_SPRITE] -= trim[0]; TopOffset[GLUSE_SPRITE] -= trim[1]; SpriteU[0] = SpriteU[1] * (trim[0] / (float)Width[GLUSE_PATCH]); SpriteV[0] = SpriteV[1] * (trim[1] / (float)Height[GLUSE_PATCH]); SpriteU[1] *= (trim[0]+trim[2]+2) / (float)Width[GLUSE_PATCH]; SpriteV[1] *= (trim[1]+trim[3]+2) / (float)Height[GLUSE_PATCH]; } } } } //=========================================================================== // // Destructor // //=========================================================================== FMaterial::~FMaterial() { for(unsigned i=0;i= size) { rect[0] = 0; rect[1] = 0; rect[2] = 1; rect[3] = 1; delete [] buffer; return true; } for(last = size-1; last >= first; last--) { if (buffer[last*4+3] != 0) break; } rect[1] = first / w; rect[3] = 1 + last/w - rect[1]; rect[0] = 0; rect[2] = w; unsigned char *bufferoff = buffer + (rect[1] * w * 4); h = rect[3]; for(int x = 0; x < w; x++) { for(int y = 0; y < h; y++) { if (bufferoff[(x+y*w)*4+3] != 0) goto outl; } rect[0]++; } outl: rect[2] -= rect[0]; for(int x = w-1; rect[2] > 1; x--) { for(int y = 0; y < h; y++) { if (bufferoff[(x+y*w)*4+3] != 0) { delete [] buffer; return true; } } rect[2]--; } delete [] buffer; return true; } //=========================================================================== // // Binds a texture to the renderer // //=========================================================================== void FMaterial::Bind(int cm, int clampmode, int translation, int overrideshader) { int usebright = false; int shaderindex = overrideshader > 0? overrideshader : mShaderIndex; int maxbound = 0; bool allowhires = tex->xScale == FRACUNIT && tex->yScale == FRACUNIT; int softwarewarp = gl_RenderState.SetupShader(tex->bHasCanvas, shaderindex, cm, tex->gl_info.shaderspeed); if (tex->bHasCanvas || tex->bWarped) clampmode = 0; else if (clampmode != -1) clampmode &= 3; else clampmode = 4; const FHardwareTexture *gltexture = mBaseLayer->Bind(0, cm, clampmode, translation, allowhires? tex:NULL, softwarewarp); if (gltexture != NULL && shaderindex > 0 && overrideshader == 0) { for(unsigned i=0;iGetID(); layer = TexMan(id); ValidateSysTexture(layer, false); } else { layer = mTextureLayers[i].texture; } layer->gl_info.SystemTexture->Bind(i+1, CM_DEFAULT, clampmode, 0, NULL, false); maxbound = i+1; } } // unbind everything from the last texture that's still active for(int i=maxbound+1; i<=mMaxBound;i++) { FHardwareTexture::Unbind(i); mMaxBound = maxbound; } } //=========================================================================== // // Binds a texture to the renderer // //=========================================================================== void FMaterial::BindPatch(int cm, int translation, int overrideshader) { int usebright = false; int shaderindex = overrideshader > 0? overrideshader : mShaderIndex; int maxbound = 0; int softwarewarp = gl_RenderState.SetupShader(tex->bHasCanvas, shaderindex, cm, tex->gl_info.shaderspeed); const FHardwareTexture *glpatch = mBaseLayer->BindPatch(0, cm, translation, softwarewarp); // The only multitexture effect usable on sprites is the brightmap. if (glpatch != NULL && shaderindex == 3) { mTextureLayers[0].texture->gl_info.SystemTexture->BindPatch(1, CM_DEFAULT, 0, 0); maxbound = 1; } // unbind everything from the last texture that's still active for(int i=maxbound+1; i<=mMaxBound;i++) { FHardwareTexture::Unbind(i); mMaxBound = maxbound; } } //=========================================================================== // // // //=========================================================================== void FMaterial::Precache() { if (tex->UseType==FTexture::TEX_Sprite) { BindPatch(CM_DEFAULT, 0); } else { int cached = 0; for(int i=0;i<4;i++) { if (mBaseLayer->gltexture[i] != 0) { Bind (CM_DEFAULT, i, 0); cached++; } if (cached == 0) Bind(CM_DEFAULT, -1, 0); } } } //=========================================================================== // // This function is needed here to temporarily manipulate the texture // for per-wall scaling so that the coordinate functions return proper // results. Doing this here is much easier than having the calling code // make these calculations. // //=========================================================================== void FMaterial::GetTexCoordInfo(FTexCoordInfo *tci, fixed_t x, fixed_t y) const { if (x == FRACUNIT) { tci->mRenderWidth = RenderWidth[GLUSE_TEXTURE]; tci->mScaleX = tex->xScale; tci->mTempScaleX = FRACUNIT; } else { fixed_t scale_x = FixedMul(x, tex->xScale); int foo = (Width[GLUSE_TEXTURE] << 17) / scale_x; tci->mRenderWidth = (foo >> 1) + (foo & 1); tci->mScaleX = scale_x; tci->mTempScaleX = x; } if (y == FRACUNIT) { tci->mRenderHeight = RenderHeight[GLUSE_TEXTURE]; tci->mScaleY = tex->yScale; tci->mTempScaleY = FRACUNIT; } else { fixed_t scale_y = FixedMul(y, tex->yScale); int foo = (Height[GLUSE_TEXTURE] << 17) / scale_y; tci->mRenderHeight = (foo >> 1) + (foo & 1); tci->mScaleY = scale_y; tci->mTempScaleY = y; } if (tex->bHasCanvas) { tci->mScaleY = -tci->mScaleY; tci->mRenderHeight = -tci->mRenderHeight; } tci->mWorldPanning = tex->bWorldPanning; tci->mWidth = Width[GLUSE_TEXTURE]; } //=========================================================================== // // // //=========================================================================== int FMaterial::GetAreas(FloatRect **pAreas) const { if (mShaderIndex == 0) // texture splitting can only be done if there's no attached effects { FTexture *tex = mBaseLayer->tex; *pAreas = tex->gl_info.areas; return tex->gl_info.areacount; } else { return 0; } } //=========================================================================== // // // //=========================================================================== void FMaterial::BindToFrameBuffer() { if (mBaseLayer->gltexture == NULL) { // must create the hardware texture first mBaseLayer->Bind(0, CM_DEFAULT, 0, 0, NULL, 0); FHardwareTexture::Unbind(0); } mBaseLayer->gltexture[0]->BindToFrameBuffer(); } //=========================================================================== // // GetRect // //=========================================================================== void FMaterial::GetRect(FloatRect * r, ETexUse i) const { r->left = -GetScaledLeftOffsetFloat(i); r->top = -GetScaledTopOffsetFloat(i); r->width = GetScaledWidthFloat(i); r->height = GetScaledHeightFloat(i); } //========================================================================== // // Gets a texture from the texture manager and checks its validity for // GL rendering. // //========================================================================== FMaterial * FMaterial::ValidateTexture(FTexture * tex) { if (tex && tex->UseType!=FTexture::TEX_Null) { FMaterial *gltex = tex->gl_info.Material; if (gltex == NULL) { //@sync-tex gltex = new FMaterial(tex, false); } return gltex; } return NULL; } FMaterial * FMaterial::ValidateTexture(FTextureID no, bool translate) { return ValidateTexture(translate? TexMan(no) : TexMan[no]); } //========================================================================== // // Flushes all hardware dependent data // //========================================================================== void FMaterial::FlushAll() { for(int i=mMaterials.Size()-1;i>=0;i--) { mMaterials[i]->Clean(true); } // This is for shader layers. All shader layers must be managed by the texture manager // so this will catch everything. for(int i=TexMan.NumTextures()-1;i>=0;i--) { FGLTexture *gltex = TexMan.ByIndex(i)->gl_info.SystemTexture; if (gltex != NULL) gltex->Clean(true); } } //========================================================================== // // Prints some texture info // //========================================================================== int FGLTexture::Dump(int i) { int cnt = 0; int lump = tex->GetSourceLump(); Printf(PRINT_LOG, "Texture '%s' (Index %d, Lump %d, Name '%s'):\n", tex->Name, i, lump, Wads.GetLumpFullName(lump)); if (hirestexture) Printf(PRINT_LOG, "\tHirestexture\n"); if (glpatch) Printf(PRINT_LOG, "\tPatch\n"),cnt++; if (gltexture[0]) Printf(PRINT_LOG, "\tTexture (x:no, y:no )\n"),cnt++; if (gltexture[1]) Printf(PRINT_LOG, "\tTexture (x:yes, y:no )\n"),cnt++; if (gltexture[2]) Printf(PRINT_LOG, "\tTexture (x:no, y:yes)\n"),cnt++; if (gltexture[3]) Printf(PRINT_LOG, "\tTexture (x:yes, y:yes)\n"),cnt++; if (gltexture[4]) Printf(PRINT_LOG, "\tTexture precache\n"),cnt++; return cnt; } CCMD(textureinfo) { int cnth = 0, cntt = 0, pix = 0; for(int i=0; igl_info.SystemTexture; if (systex != NULL) { int cnt = systex->Dump(i); cnth+=cnt; cntt++; pix += cnt * tex->GetWidth() * tex->GetHeight(); } } Printf(PRINT_LOG, "%d system textures, %d hardware textures, %d pixels\n", cntt, cnth, pix); }