gzdoom-gles/src/gl/textures/gl_material.cpp
2013-06-23 09:49:34 +02:00

1123 lines
30 KiB
C++

/*
** 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_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<FWarpTexture*>(tex)->GetSpeed();
static_cast<FWarpTexture*>(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<<ds_xbits));
}
}
timebase = DWORD(r_FrameTime*Speed*32/28);
int y;
for (y = ysize-1; y >= 0; y--)
{
int xt, xf = (finesine[(timebase+y*128)&FINEMASK]>>13) & xmask;
DWORD *source = out + (y<<ds_xbits);
DWORD *dest = linebuffer;
for (xt = xsize; xt; xt--, xf = (xf+1)&xmask)
{
*dest++ = *(source+xf);
}
memcpy (out+y*xsize, linebuffer, xsize*sizeof(DWORD));
}
}
else
{
int ybits;
for(ybits=-1,i=ysize; i; i>>=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))
{
gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, (clampmode & GLT_CLAMPX)? GL_CLAMP_TO_EDGE : GL_REPEAT);
gl.TexParameteri(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, h;
// 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;
}
gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, (clampmode & GLT_CLAMPX)? GL_CLAMP_TO_EDGE : GL_REPEAT);
gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, (clampmode & GLT_CLAMPY)? GL_CLAMP_TO_EDGE : GL_REPEAT);
delete[] buffer;
}
if (tex->bHasCanvas) static_cast<FCanvasTexture*>(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;
gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
gl.TexParameteri(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 *> 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<FWarpTexture*>(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)
{
// 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
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<mMaterials.Size();i++)
{
if (mMaterials[i]==this)
{
mMaterials.Delete(i);
break;
}
}
}
//===========================================================================
//
// Finds gaps in the texture which can be skipped by the renderer
// This was mainly added to speed up one area in E4M6 of 007LTSD
//
//===========================================================================
bool FMaterial::TrimBorders(int *rect)
{
PalEntry col;
int w;
int h;
unsigned char *buffer = CreateTexBuffer(CM_DEFAULT, 0, w, h);
if (buffer == NULL)
{
return false;
}
if (w != Width[GLUSE_TEXTURE] || h != Height[GLUSE_TEXTURE])
{
// external Hires replacements cannot be trimmed.
delete [] buffer;
return false;
}
int size = w*h;
int first, last;
for(first = 0; first < size; first++)
{
if (buffer[first*4+3] != 0) break;
}
if (first >= 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;i<mTextureLayers.Size();i++)
{
FTexture *layer;
if (mTextureLayers[i].animated)
{
FTextureID id = mTextureLayers[i].texture->GetID();
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; i<TexMan.NumTextures(); i++)
{
FTexture *tex = TexMan.ByIndex(i);
FGLTexture *systex = tex->gl_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);
}