2013-06-23 07:49:34 +00:00
|
|
|
/*
|
|
|
|
** 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"
|
|
|
|
|
2013-09-03 16:29:39 +00:00
|
|
|
#include "gl/system/gl_interface.h"
|
2013-06-23 07:49:34 +00:00
|
|
|
#include "gl/system/gl_framebuffer.h"
|
|
|
|
#include "gl/renderer/gl_lightdata.h"
|
2014-08-22 21:50:38 +00:00
|
|
|
#include "gl/renderer/gl_renderer.h"
|
2013-06-23 07:49:34 +00:00
|
|
|
#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"
|
2014-08-22 21:50:38 +00:00
|
|
|
#include "gl/textures/gl_samplers.h"
|
2013-06-23 07:49:34 +00:00
|
|
|
#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)
|
|
|
|
{
|
2014-08-22 21:50:38 +00:00
|
|
|
assert(tx->gl_info.SystemTexture[expandpatches] == NULL);
|
2013-06-23 07:49:34 +00:00
|
|
|
tex = tx;
|
|
|
|
|
2014-08-22 21:50:38 +00:00
|
|
|
mHwTexture = NULL;
|
|
|
|
HiresLump = -1;
|
2013-06-23 07:49:34 +00:00
|
|
|
hirestexture = NULL;
|
|
|
|
bHasColorkey = false;
|
|
|
|
bIsTransparent = -1;
|
2015-04-04 11:37:55 +00:00
|
|
|
bExpandFlag = expandpatches;
|
2014-08-22 21:50:38 +00:00
|
|
|
tex->gl_info.SystemTexture[expandpatches] = this;
|
2013-06-23 07:49:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// Destructor
|
|
|
|
//
|
|
|
|
//===========================================================================
|
|
|
|
|
|
|
|
FGLTexture::~FGLTexture()
|
|
|
|
{
|
|
|
|
Clean(true);
|
|
|
|
if (hirestexture) delete hirestexture;
|
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// Checks for the presence of a hires texture replacement and loads it
|
|
|
|
//
|
|
|
|
//==========================================================================
|
2014-09-09 11:21:36 +00:00
|
|
|
unsigned char *FGLTexture::LoadHiresTexture(FTexture *tex, int *width, int *height)
|
2013-06-23 07:49:34 +00:00
|
|
|
{
|
2015-04-04 11:37:55 +00:00
|
|
|
if (bExpandFlag) return NULL; // doesn't work for expanded textures
|
|
|
|
|
2013-06-23 07:49:34 +00:00
|
|
|
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);
|
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
2014-08-22 21:50:38 +00:00
|
|
|
if (mHwTexture)
|
2013-06-23 07:49:34 +00:00
|
|
|
{
|
2014-08-22 21:50:38 +00:00
|
|
|
if (!all) mHwTexture->Clean(false);
|
2013-06-23 07:49:34 +00:00
|
|
|
else
|
|
|
|
{
|
2014-08-22 21:50:38 +00:00
|
|
|
delete mHwTexture;
|
|
|
|
mHwTexture = NULL;
|
2013-06-23 07:49:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// Initializes the buffer for the texture data
|
|
|
|
//
|
|
|
|
//===========================================================================
|
|
|
|
|
2015-04-04 11:37:55 +00:00
|
|
|
unsigned char * FGLTexture::CreateTexBuffer(int translation, int & w, int & h, FTexture *hirescheck, bool createexpanded)
|
2013-06-23 07:49:34 +00:00
|
|
|
{
|
|
|
|
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)
|
|
|
|
{
|
2014-09-09 11:21:36 +00:00
|
|
|
buffer = LoadHiresTexture (hirescheck, &w, &h);
|
2013-06-23 07:49:34 +00:00
|
|
|
if (buffer)
|
|
|
|
{
|
|
|
|
return buffer;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-04 15:50:22 +00:00
|
|
|
int exx = bExpandFlag && createexpanded;
|
2015-04-04 11:37:55 +00:00
|
|
|
|
|
|
|
W = w = tex->GetWidth() + 2 * exx;
|
2015-04-04 15:50:22 +00:00
|
|
|
H = h = tex->GetHeight() + 2 * exx;
|
2013-06-23 07:49:34 +00:00
|
|
|
|
|
|
|
|
|
|
|
buffer=new unsigned char[W*(H+1)*4];
|
|
|
|
memset(buffer, 0, W * (H+1) * 4);
|
|
|
|
|
|
|
|
FGLBitmap bmp(buffer, W*4, W, H);
|
2014-05-11 17:44:19 +00:00
|
|
|
bmp.SetTranslationInfo(translation);
|
2013-06-23 07:49:34 +00:00
|
|
|
|
|
|
|
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);
|
2015-04-04 15:50:22 +00:00
|
|
|
int trans = tex->CopyTrueColorPixels(&imgCreate, exx, exx);
|
2013-06-23 07:49:34 +00:00
|
|
|
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)
|
|
|
|
{
|
2015-04-04 15:50:22 +00:00
|
|
|
int trans = tex->CopyTrueColorPixels(&bmp, exx, exx);
|
2013-06-23 07:49:34 +00:00
|
|
|
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. ;)
|
2015-04-04 15:50:22 +00:00
|
|
|
tex->FTexture::CopyTrueColorPixels(&bmp, exx, exx);
|
2013-06-23 07:49:34 +00:00
|
|
|
bIsTransparent = 0;
|
|
|
|
}
|
|
|
|
|
2015-04-04 11:37:55 +00:00
|
|
|
// if we just want the texture for some checks there's no need for upsampling.
|
|
|
|
if (!createexpanded) return buffer;
|
|
|
|
|
2013-06-23 07:49:34 +00:00
|
|
|
// [BB] The hqnx upsampling (not the scaleN one) destroys partial transparency, don't upsamle textures using it.
|
2014-06-21 13:50:32 +00:00
|
|
|
// [BB] Potentially upsample the buffer.
|
2014-09-09 11:21:36 +00:00
|
|
|
return gl_CreateUpsampledTextureBuffer ( tex, buffer, W, H, w, h, !!bIsTransparent);
|
2013-06-23 07:49:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// Create hardware texture for world use
|
|
|
|
//
|
|
|
|
//===========================================================================
|
|
|
|
|
2014-08-22 21:50:38 +00:00
|
|
|
FHardwareTexture *FGLTexture::CreateHwTexture()
|
2013-06-23 07:49:34 +00:00
|
|
|
{
|
|
|
|
if (tex->UseType==FTexture::TEX_Null) return NULL; // Cannot register a NULL texture
|
2014-08-22 21:50:38 +00:00
|
|
|
if (mHwTexture == NULL)
|
2013-06-23 07:49:34 +00:00
|
|
|
{
|
2015-04-04 15:50:22 +00:00
|
|
|
mHwTexture = new FHardwareTexture(tex->GetWidth() + bExpandFlag*2, tex->GetHeight() + bExpandFlag*2, tex->gl_info.bNoCompress);
|
2013-06-23 07:49:34 +00:00
|
|
|
}
|
2014-08-22 21:50:38 +00:00
|
|
|
return mHwTexture;
|
2013-06-23 07:49:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// Binds a texture to the renderer
|
|
|
|
//
|
|
|
|
//===========================================================================
|
|
|
|
|
2014-09-09 11:21:36 +00:00
|
|
|
const FHardwareTexture *FGLTexture::Bind(int texunit, int clampmode, int translation, FTexture *hirescheck)
|
2013-06-23 07:49:34 +00:00
|
|
|
{
|
|
|
|
int usebright = false;
|
|
|
|
|
|
|
|
if (translation <= 0) translation = -translation;
|
|
|
|
else translation = GLTranslationPalette::GetInternalTranslation(translation);
|
|
|
|
|
2014-08-22 21:50:38 +00:00
|
|
|
bool needmipmap = (clampmode <= CLAMP_XY);
|
2013-06-23 07:49:34 +00:00
|
|
|
|
2014-08-22 21:50:38 +00:00
|
|
|
FHardwareTexture *hwtex = CreateHwTexture();
|
2013-06-23 07:49:34 +00:00
|
|
|
|
|
|
|
if (hwtex)
|
|
|
|
{
|
2014-06-21 13:50:32 +00:00
|
|
|
// Texture has become invalid
|
|
|
|
if ((!tex->bHasCanvas && !tex->bWarped) && tex->CheckModified())
|
2013-06-23 07:49:34 +00:00
|
|
|
{
|
|
|
|
Clean(true);
|
2014-08-22 21:50:38 +00:00
|
|
|
hwtex = CreateHwTexture();
|
2013-06-23 07:49:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Bind it to the system.
|
2014-09-09 11:21:36 +00:00
|
|
|
if (!hwtex->Bind(texunit, translation, needmipmap))
|
2013-06-23 07:49:34 +00:00
|
|
|
{
|
|
|
|
|
2013-08-28 06:33:11 +00:00
|
|
|
int w=0, h=0;
|
2013-06-23 07:49:34 +00:00
|
|
|
|
|
|
|
// Create this texture
|
|
|
|
unsigned char * buffer = NULL;
|
|
|
|
|
|
|
|
if (!tex->bHasCanvas)
|
|
|
|
{
|
2014-09-09 11:21:36 +00:00
|
|
|
buffer = CreateTexBuffer(translation, w, h, hirescheck);
|
2013-06-23 07:49:34 +00:00
|
|
|
tex->ProcessData(buffer, w, h, false);
|
|
|
|
}
|
2014-09-09 11:21:36 +00:00
|
|
|
if (!hwtex->CreateTexture(buffer, w, h, texunit, needmipmap, translation))
|
2013-06-23 07:49:34 +00:00
|
|
|
{
|
|
|
|
// could not create texture
|
|
|
|
delete[] buffer;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
delete[] buffer;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tex->bHasCanvas) static_cast<FCanvasTexture*>(tex)->NeedUpdate();
|
2014-08-22 21:50:38 +00:00
|
|
|
GLRenderer->mSamplerManager->Bind(texunit, clampmode);
|
2013-06-23 07:49:34 +00:00
|
|
|
return hwtex;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
//
|
|
|
|
//
|
|
|
|
//===========================================================================
|
|
|
|
|
2016-04-07 22:19:51 +00:00
|
|
|
float FTexCoordInfo::RowOffset(float rowoffset) const
|
2013-06-23 07:49:34 +00:00
|
|
|
{
|
2016-04-07 23:42:43 +00:00
|
|
|
if (mTempScale.Y == 1.f)
|
2013-06-23 07:49:34 +00:00
|
|
|
{
|
2016-04-07 23:42:43 +00:00
|
|
|
if (mScale.Y == 1.f || mWorldPanning) return rowoffset;
|
|
|
|
else return rowoffset * mScale.Y;
|
2013-06-23 07:49:34 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2016-04-07 23:42:43 +00:00
|
|
|
if (mWorldPanning) return rowoffset * mTempScale.Y;
|
|
|
|
else return rowoffset * mScale.Y;
|
2013-06-23 07:49:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
//
|
|
|
|
//
|
|
|
|
//===========================================================================
|
|
|
|
|
2016-04-07 22:19:51 +00:00
|
|
|
float FTexCoordInfo::TextureOffset(float textureoffset) const
|
2013-06-23 07:49:34 +00:00
|
|
|
{
|
2016-04-07 23:42:43 +00:00
|
|
|
if (mTempScale.X == 1.f)
|
2013-06-23 07:49:34 +00:00
|
|
|
{
|
2016-04-07 23:42:43 +00:00
|
|
|
if (mScale.X == 1.f || mWorldPanning) return textureoffset;
|
|
|
|
else return textureoffset * mScale.X;
|
2013-06-23 07:49:34 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2016-04-07 23:42:43 +00:00
|
|
|
if (mWorldPanning) return textureoffset * mTempScale.X;
|
|
|
|
else return textureoffset * mScale.X;
|
2013-06-23 07:49:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// Returns the size for which texture offset coordinates are used.
|
|
|
|
//
|
|
|
|
//===========================================================================
|
|
|
|
|
2016-04-07 23:42:43 +00:00
|
|
|
float FTexCoordInfo::TextureAdjustWidth() const
|
2013-06-23 07:49:34 +00:00
|
|
|
{
|
|
|
|
if (mWorldPanning)
|
|
|
|
{
|
2016-04-07 23:42:43 +00:00
|
|
|
if (mTempScale.X == 1.f) return mRenderWidth;
|
|
|
|
else return mWidth * mTempScale.X;
|
2013-06-23 07:49:34 +00:00
|
|
|
}
|
|
|
|
else return mWidth;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
//
|
|
|
|
//
|
|
|
|
//===========================================================================
|
|
|
|
FGLTexture * FMaterial::ValidateSysTexture(FTexture * tex, bool expand)
|
|
|
|
{
|
|
|
|
if (tex && tex->UseType!=FTexture::TEX_Null)
|
|
|
|
{
|
2014-08-22 21:50:38 +00:00
|
|
|
FGLTexture *gltex = tex->gl_info.SystemTexture[expand];
|
2013-06-23 07:49:34 +00:00
|
|
|
if (gltex == NULL)
|
|
|
|
{
|
|
|
|
gltex = new FGLTexture(tex, expand);
|
|
|
|
}
|
|
|
|
return gltex;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// Constructor
|
|
|
|
//
|
|
|
|
//===========================================================================
|
|
|
|
TArray<FMaterial *> FMaterial::mMaterials;
|
|
|
|
int FMaterial::mMaxBound;
|
|
|
|
|
2014-08-22 21:50:38 +00:00
|
|
|
FMaterial::FMaterial(FTexture * tx, bool expanded)
|
2013-06-23 07:49:34 +00:00
|
|
|
{
|
|
|
|
mShaderIndex = 0;
|
2014-09-09 08:03:34 +00:00
|
|
|
tex = tx;
|
2013-06-23 07:49:34 +00:00
|
|
|
|
|
|
|
// TODO: apply custom shader object here
|
|
|
|
/* if (tx->CustomShaderDefinition)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
else
|
|
|
|
*/
|
|
|
|
if (tx->bWarped)
|
|
|
|
{
|
|
|
|
mShaderIndex = tx->bWarped;
|
|
|
|
tx->gl_info.shaderspeed = static_cast<FWarpTexture*>(tx)->GetSpeed();
|
|
|
|
}
|
|
|
|
else if (tx->bHasCanvas)
|
|
|
|
{
|
|
|
|
}
|
2014-06-21 13:50:32 +00:00
|
|
|
else
|
2013-06-23 07:49:34 +00:00
|
|
|
{
|
|
|
|
if (tx->gl_info.shaderindex >= FIRST_USER_SHADER)
|
|
|
|
{
|
|
|
|
mShaderIndex = tx->gl_info.shaderindex;
|
|
|
|
}
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-04-04 15:50:22 +00:00
|
|
|
mBaseLayer = ValidateSysTexture(tx, expanded);
|
2013-06-23 07:49:34 +00:00
|
|
|
|
|
|
|
|
2014-08-22 21:50:38 +00:00
|
|
|
mWidth = tx->GetWidth();
|
|
|
|
mHeight = tx->GetHeight();
|
|
|
|
mLeftOffset = tx->LeftOffset;
|
|
|
|
mTopOffset = tx->TopOffset;
|
|
|
|
mRenderWidth = tx->GetScaledWidth();
|
|
|
|
mRenderHeight = tx->GetScaledHeight();
|
|
|
|
mSpriteU[0] = mSpriteV[0] = 0.f;
|
|
|
|
mSpriteU[1] = mSpriteV[1] = 1.f;
|
2013-06-23 07:49:34 +00:00
|
|
|
|
2014-06-21 13:50:32 +00:00
|
|
|
FTexture *basetex = tx->GetRedirect(false);
|
2015-04-04 15:50:22 +00:00
|
|
|
// allow the redirect only if the textute is not expanded or the scale matches.
|
2016-03-29 09:26:33 +00:00
|
|
|
if (!expanded || (tx->Scale.X == basetex->Scale.X && tx->Scale.Y == basetex->Scale.Y))
|
2015-04-04 15:50:22 +00:00
|
|
|
{
|
|
|
|
mBaseLayer = ValidateSysTexture(basetex, expanded);
|
|
|
|
}
|
2014-08-22 21:50:38 +00:00
|
|
|
|
2016-03-29 09:26:33 +00:00
|
|
|
float fxScale = tx->Scale.X;
|
|
|
|
float fyScale = tx->Scale.Y;
|
2015-04-04 11:37:55 +00:00
|
|
|
|
2014-08-22 21:50:38 +00:00
|
|
|
// mSpriteRect is for positioning the sprite in the scene.
|
2015-04-04 11:37:55 +00:00
|
|
|
mSpriteRect.left = -mLeftOffset / fxScale;
|
|
|
|
mSpriteRect.top = -mTopOffset / fyScale;
|
|
|
|
mSpriteRect.width = mWidth / fxScale;
|
|
|
|
mSpriteRect.height = mHeight / fyScale;
|
2014-08-22 21:50:38 +00:00
|
|
|
|
|
|
|
if (expanded)
|
2013-06-23 07:49:34 +00:00
|
|
|
{
|
|
|
|
// a little adjustment to make sprites look better with texture filtering:
|
2015-04-04 15:50:22 +00:00
|
|
|
// create a 1 pixel wide empty frame around them.
|
2014-04-06 12:35:44 +00:00
|
|
|
int trim[4];
|
2015-04-04 10:35:10 +00:00
|
|
|
bool trimmed = TrimBorders(trim); // get the trim size before adding the empty frame
|
|
|
|
|
|
|
|
int oldwidth = mWidth;
|
|
|
|
int oldheight = mHeight;
|
|
|
|
|
2015-04-04 15:50:22 +00:00
|
|
|
mWidth+=2;
|
|
|
|
mHeight+=2;
|
|
|
|
mLeftOffset+=1;
|
|
|
|
mTopOffset+=1;
|
2015-04-04 10:35:10 +00:00
|
|
|
mRenderWidth = mRenderWidth * mWidth / oldwidth;
|
|
|
|
mRenderHeight = mRenderHeight * mHeight / oldheight;
|
|
|
|
|
2015-04-04 11:37:55 +00:00
|
|
|
// Reposition the sprite with the frame considered
|
|
|
|
mSpriteRect.left = -mLeftOffset / fxScale;
|
|
|
|
mSpriteRect.top = -mTopOffset / fyScale;
|
|
|
|
mSpriteRect.width = mWidth / fxScale;
|
|
|
|
mSpriteRect.height = mHeight / fyScale;
|
|
|
|
|
2015-04-04 10:35:10 +00:00
|
|
|
if (trimmed)
|
2014-04-06 12:35:44 +00:00
|
|
|
{
|
2015-04-04 11:37:55 +00:00
|
|
|
mSpriteRect.left += trim[0] / fxScale;
|
|
|
|
mSpriteRect.top += trim[1] / fyScale;
|
|
|
|
|
|
|
|
mSpriteRect.width -= (oldwidth - trim[2]) / fxScale;
|
|
|
|
mSpriteRect.height -= (oldheight - trim[3]) / fyScale;
|
2014-08-22 21:50:38 +00:00
|
|
|
|
|
|
|
mSpriteU[0] = trim[0] / (float)mWidth;
|
|
|
|
mSpriteV[0] = trim[1] / (float)mHeight;
|
2015-04-04 11:37:55 +00:00
|
|
|
mSpriteU[1] -= (oldwidth - trim[0] - trim[2]) / (float)mWidth;
|
|
|
|
mSpriteV[1] -= (oldheight - trim[1] - trim[3]) / (float)mHeight;
|
2015-04-04 10:35:10 +00:00
|
|
|
}
|
2013-06-23 07:49:34 +00:00
|
|
|
}
|
2014-08-22 21:50:38 +00:00
|
|
|
|
|
|
|
mTextureLayers.ShrinkToFit();
|
|
|
|
mMaxBound = -1;
|
|
|
|
mMaterials.Push(this);
|
|
|
|
tx->gl_info.Material[expanded] = this;
|
|
|
|
if (tx->bHasCanvas) tx->gl_info.mIsTransparent = 0;
|
|
|
|
mExpanded = expanded;
|
2013-06-23 07:49:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// 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;
|
|
|
|
|
2015-04-04 11:37:55 +00:00
|
|
|
unsigned char *buffer = CreateTexBuffer(0, w, h, false, false);
|
2013-06-23 07:49:34 +00:00
|
|
|
|
|
|
|
if (buffer == NULL)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
2014-08-22 21:50:38 +00:00
|
|
|
if (w != mWidth || h != mHeight)
|
2013-06-23 07:49:34 +00:00
|
|
|
{
|
|
|
|
// external Hires replacements cannot be trimmed.
|
|
|
|
delete [] buffer;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
int size = w*h;
|
2015-04-04 11:37:55 +00:00
|
|
|
if (size == 1) return false;
|
2013-06-23 07:49:34 +00:00
|
|
|
|
|
|
|
int first, last;
|
|
|
|
|
|
|
|
for(first = 0; first < size; first++)
|
|
|
|
{
|
|
|
|
if (buffer[first*4+3] != 0) break;
|
|
|
|
}
|
|
|
|
if (first >= size)
|
|
|
|
{
|
2015-04-04 11:37:55 +00:00
|
|
|
// completely empty
|
2013-06-23 07:49:34 +00:00
|
|
|
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
|
|
|
|
//
|
|
|
|
//===========================================================================
|
|
|
|
|
2014-08-30 13:34:14 +00:00
|
|
|
static FMaterial *last;
|
|
|
|
static int lastclamp;
|
|
|
|
static int lasttrans;
|
|
|
|
|
|
|
|
|
2014-09-09 11:21:36 +00:00
|
|
|
void FMaterial::Bind(int clampmode, int translation)
|
2014-09-09 10:00:42 +00:00
|
|
|
{
|
2014-08-30 13:34:14 +00:00
|
|
|
// avoid rebinding the same texture multiple times.
|
2014-09-09 11:21:36 +00:00
|
|
|
if (this == last && lastclamp == clampmode && translation == lasttrans) return;
|
2014-08-30 13:34:14 +00:00
|
|
|
last = this;
|
|
|
|
lastclamp = clampmode;
|
|
|
|
lasttrans = translation;
|
|
|
|
|
|
|
|
int usebright = false;
|
2013-06-23 07:49:34 +00:00
|
|
|
int maxbound = 0;
|
2016-03-29 09:26:33 +00:00
|
|
|
bool allowhires = tex->Scale.X == 1 && tex->Scale.Y == 1 && clampmode <= CLAMP_XY && !mExpanded;
|
2013-06-23 07:49:34 +00:00
|
|
|
|
2014-08-22 21:50:38 +00:00
|
|
|
if (tex->bHasCanvas) clampmode = CLAMP_CAMTEX;
|
|
|
|
else if (tex->bWarped && clampmode <= CLAMP_XY) clampmode = CLAMP_NONE;
|
2013-06-23 07:49:34 +00:00
|
|
|
|
2014-09-09 11:21:36 +00:00
|
|
|
const FHardwareTexture *gltexture = mBaseLayer->Bind(0, clampmode, translation, allowhires? tex:NULL);
|
2014-09-09 10:00:42 +00:00
|
|
|
if (gltexture != NULL)
|
2013-06-23 07:49:34 +00:00
|
|
|
{
|
|
|
|
for(unsigned i=0;i<mTextureLayers.Size();i++)
|
|
|
|
{
|
|
|
|
FTexture *layer;
|
|
|
|
if (mTextureLayers[i].animated)
|
|
|
|
{
|
2014-05-15 17:27:22 +00:00
|
|
|
FTextureID id = mTextureLayers[i].texture->id;
|
2013-06-23 07:49:34 +00:00
|
|
|
layer = TexMan(id);
|
2014-08-22 21:50:38 +00:00
|
|
|
ValidateSysTexture(layer, mExpanded);
|
2013-06-23 07:49:34 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
layer = mTextureLayers[i].texture;
|
|
|
|
}
|
2014-09-09 11:21:36 +00:00
|
|
|
layer->gl_info.SystemTexture[mExpanded]->Bind(i+1, clampmode, 0, NULL);
|
2013-06-23 07:49:34 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
//
|
|
|
|
//
|
|
|
|
//===========================================================================
|
|
|
|
void FMaterial::Precache()
|
|
|
|
{
|
2014-09-09 11:21:36 +00:00
|
|
|
Bind(0, 0);
|
2013-06-23 07:49:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
//
|
2014-08-22 21:50:38 +00:00
|
|
|
// Retrieve texture coordinate info for per-wall scaling
|
2013-06-23 07:49:34 +00:00
|
|
|
//
|
|
|
|
//===========================================================================
|
|
|
|
|
2016-04-07 23:42:43 +00:00
|
|
|
void FMaterial::GetTexCoordInfo(FTexCoordInfo *tci, float x, float y) const
|
2013-06-23 07:49:34 +00:00
|
|
|
{
|
2016-04-07 23:42:43 +00:00
|
|
|
if (x == 1.f)
|
2013-06-23 07:49:34 +00:00
|
|
|
{
|
2014-08-22 21:50:38 +00:00
|
|
|
tci->mRenderWidth = mRenderWidth;
|
2016-04-07 23:42:43 +00:00
|
|
|
tci->mScale.X = tex->Scale.X;
|
|
|
|
tci->mTempScale.X = 1.f;
|
2013-06-23 07:49:34 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2016-04-07 23:42:43 +00:00
|
|
|
float scale_x = x * tex->Scale.X;
|
|
|
|
tci->mRenderWidth = xs_CeilToInt(mWidth / scale_x);
|
|
|
|
tci->mScale.X = scale_x;
|
|
|
|
tci->mTempScale.X = x;
|
2013-06-23 07:49:34 +00:00
|
|
|
}
|
|
|
|
|
2016-04-07 23:42:43 +00:00
|
|
|
if (y == 1.f)
|
2013-06-23 07:49:34 +00:00
|
|
|
{
|
2014-08-22 21:50:38 +00:00
|
|
|
tci->mRenderHeight = mRenderHeight;
|
2016-04-07 23:42:43 +00:00
|
|
|
tci->mScale.Y = tex->Scale.Y;
|
|
|
|
tci->mTempScale.Y = 1.f;
|
2013-06-23 07:49:34 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2016-04-07 23:42:43 +00:00
|
|
|
float scale_y = y * tex->Scale.Y;
|
|
|
|
tci->mRenderHeight = xs_CeilToInt(mHeight / scale_y);
|
|
|
|
tci->mScale.Y = scale_y;
|
|
|
|
tci->mTempScale.Y = y;
|
2013-06-23 07:49:34 +00:00
|
|
|
}
|
|
|
|
if (tex->bHasCanvas)
|
|
|
|
{
|
2016-04-07 23:42:43 +00:00
|
|
|
tci->mScale.Y = -tci->mScale.Y;
|
2013-06-23 07:49:34 +00:00
|
|
|
tci->mRenderHeight = -tci->mRenderHeight;
|
|
|
|
}
|
|
|
|
tci->mWorldPanning = tex->bWorldPanning;
|
2014-08-22 21:50:38 +00:00
|
|
|
tci->mWidth = mWidth;
|
2013-06-23 07:49:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
//
|
|
|
|
//
|
|
|
|
//===========================================================================
|
|
|
|
|
|
|
|
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()
|
|
|
|
{
|
2014-08-22 21:50:38 +00:00
|
|
|
if (mBaseLayer->mHwTexture == NULL)
|
2013-06-23 07:49:34 +00:00
|
|
|
{
|
|
|
|
// must create the hardware texture first
|
2014-09-09 11:21:36 +00:00
|
|
|
mBaseLayer->Bind(0, 0, 0, NULL);
|
2013-06-23 07:49:34 +00:00
|
|
|
FHardwareTexture::Unbind(0);
|
2014-08-31 17:00:17 +00:00
|
|
|
ClearLastTexture();
|
2013-06-23 07:49:34 +00:00
|
|
|
}
|
2014-08-22 21:50:38 +00:00
|
|
|
mBaseLayer->mHwTexture->BindToFrameBuffer();
|
2013-06-23 07:49:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// Gets a texture from the texture manager and checks its validity for
|
|
|
|
// GL rendering.
|
|
|
|
//
|
|
|
|
//==========================================================================
|
|
|
|
|
2014-08-22 21:50:38 +00:00
|
|
|
FMaterial * FMaterial::ValidateTexture(FTexture * tex, bool expand)
|
2013-06-23 07:49:34 +00:00
|
|
|
{
|
2015-04-04 15:50:22 +00:00
|
|
|
again:
|
2013-06-23 07:49:34 +00:00
|
|
|
if (tex && tex->UseType!=FTexture::TEX_Null)
|
|
|
|
{
|
2015-04-04 15:50:22 +00:00
|
|
|
if (tex->gl_info.bNoExpand) expand = false;
|
|
|
|
|
2014-08-22 21:50:38 +00:00
|
|
|
FMaterial *gltex = tex->gl_info.Material[expand];
|
2013-06-23 07:49:34 +00:00
|
|
|
if (gltex == NULL)
|
|
|
|
{
|
2015-04-04 15:50:22 +00:00
|
|
|
if (expand)
|
2015-01-08 17:44:55 +00:00
|
|
|
{
|
2015-04-04 15:50:22 +00:00
|
|
|
if (tex->bWarped || tex->bHasCanvas || tex->gl_info.shaderindex >= FIRST_USER_SHADER)
|
|
|
|
{
|
|
|
|
tex->gl_info.bNoExpand = true;
|
|
|
|
goto again;
|
|
|
|
}
|
|
|
|
if (tex->gl_info.Brightmap != NULL &&
|
|
|
|
(tex->GetWidth() != tex->gl_info.Brightmap->GetWidth() ||
|
|
|
|
tex->GetHeight() != tex->gl_info.Brightmap->GetHeight())
|
|
|
|
)
|
|
|
|
{
|
|
|
|
// do not expand if the brightmap's size differs.
|
|
|
|
tex->gl_info.bNoExpand = true;
|
|
|
|
goto again;
|
|
|
|
}
|
2015-01-08 17:44:55 +00:00
|
|
|
}
|
2014-08-22 21:50:38 +00:00
|
|
|
gltex = new FMaterial(tex, expand);
|
2013-06-23 07:49:34 +00:00
|
|
|
}
|
|
|
|
return gltex;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2014-08-22 21:50:38 +00:00
|
|
|
FMaterial * FMaterial::ValidateTexture(FTextureID no, bool expand, bool translate)
|
2013-06-23 07:49:34 +00:00
|
|
|
{
|
2014-08-22 21:50:38 +00:00
|
|
|
return ValidateTexture(translate? TexMan(no) : TexMan[no], expand);
|
2013-06-23 07:49:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//==========================================================================
|
|
|
|
//
|
|
|
|
// 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--)
|
|
|
|
{
|
2014-08-22 21:50:38 +00:00
|
|
|
for (int j = 0; j < 2; j++)
|
2013-06-23 07:49:34 +00:00
|
|
|
{
|
2014-08-22 21:50:38 +00:00
|
|
|
FGLTexture *gltex = TexMan.ByIndex(i)->gl_info.SystemTexture[j];
|
|
|
|
if (gltex != NULL) gltex->Clean(true);
|
2013-06-23 07:49:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-08-31 17:00:17 +00:00
|
|
|
void FMaterial::ClearLastTexture()
|
|
|
|
{
|
|
|
|
last = NULL;
|
|
|
|
}
|