mirror of
https://github.com/ZDoom/gzdoom.git
synced 2025-03-14 15:00:48 +00:00
354 lines
9.6 KiB
C++
354 lines
9.6 KiB
C++
//
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// Copyright(C) 2004-2016 Christoph Oelckers
|
|
// All rights reserved.
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU Lesser General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU Lesser General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Lesser General Public License
|
|
// along with this program. If not, see http://www.gnu.org/licenses/
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
/*
|
|
** gltexture.cpp
|
|
** Low level OpenGL texture handling. These classes are also
|
|
** containers for the various translations a texture can have.
|
|
**
|
|
*/
|
|
|
|
#include "gl_system.h"
|
|
#include "templates.h"
|
|
#include "c_cvars.h"
|
|
#include "doomtype.h"
|
|
#include "r_data/colormaps.h"
|
|
#include "hw_material.h"
|
|
|
|
#include "gl_interface.h"
|
|
#include "hwrenderer/utility/hw_cvars.h"
|
|
#include "gl/system/gl_debug.h"
|
|
#include "gl/renderer/gl_renderer.h"
|
|
#include "gl/renderer/gl_renderstate.h"
|
|
#include "gl/textures/gl_samplers.h"
|
|
|
|
namespace OpenGLRenderer
|
|
{
|
|
|
|
|
|
TexFilter_s TexFilter[]={
|
|
{GL_NEAREST, GL_NEAREST, false},
|
|
{GL_NEAREST_MIPMAP_NEAREST, GL_NEAREST, true},
|
|
{GL_LINEAR, GL_LINEAR, false},
|
|
{GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR, true},
|
|
{GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR, true},
|
|
{GL_NEAREST_MIPMAP_LINEAR, GL_NEAREST, true},
|
|
{GL_LINEAR_MIPMAP_LINEAR, GL_NEAREST, true},
|
|
};
|
|
|
|
int TexFormat[]={
|
|
GL_RGBA8,
|
|
GL_RGB5_A1,
|
|
GL_RGBA4,
|
|
GL_RGBA2,
|
|
// [BB] Added compressed texture formats.
|
|
GL_COMPRESSED_RGBA_ARB,
|
|
GL_COMPRESSED_RGBA_S3TC_DXT1_EXT,
|
|
GL_COMPRESSED_RGBA_S3TC_DXT3_EXT,
|
|
GL_COMPRESSED_RGBA_S3TC_DXT5_EXT,
|
|
};
|
|
|
|
|
|
|
|
//===========================================================================
|
|
//
|
|
// Static texture data
|
|
//
|
|
//===========================================================================
|
|
unsigned int FHardwareTexture::lastbound[FHardwareTexture::MAX_TEXTURES];
|
|
|
|
//===========================================================================
|
|
//
|
|
// Loads the texture image into the hardware
|
|
//
|
|
// NOTE: For some strange reason I was unable to find the source buffer
|
|
// should be one line higher than the actual texture. I got extremely
|
|
// strange crashes deep inside the GL driver when I didn't do it!
|
|
//
|
|
//===========================================================================
|
|
|
|
unsigned int FHardwareTexture::CreateTexture(unsigned char * buffer, int w, int h, int texunit, bool mipmap, const char *name)
|
|
{
|
|
int rh,rw;
|
|
int texformat = GL_RGBA8;// TexFormat[gl_texture_format];
|
|
bool deletebuffer=false;
|
|
|
|
/*
|
|
if (forcenocompression)
|
|
{
|
|
texformat = GL_RGBA8;
|
|
}
|
|
*/
|
|
bool firstCall = glTexID == 0;
|
|
if (firstCall)
|
|
{
|
|
glGenTextures(1, &glTexID);
|
|
}
|
|
|
|
int textureBinding = UINT_MAX;
|
|
if (texunit == -1) glGetIntegerv(GL_TEXTURE_BINDING_2D, &textureBinding);
|
|
if (texunit > 0) glActiveTexture(GL_TEXTURE0+texunit);
|
|
if (texunit >= 0) lastbound[texunit] = glTexID;
|
|
glBindTexture(GL_TEXTURE_2D, glTexID);
|
|
|
|
FGLDebug::LabelObject(GL_TEXTURE, glTexID, name);
|
|
|
|
rw = GetTexDimension(w);
|
|
rh = GetTexDimension(h);
|
|
if (glBufferID > 0)
|
|
{
|
|
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
|
|
buffer = nullptr;
|
|
}
|
|
else if (!buffer)
|
|
{
|
|
// The texture must at least be initialized if no data is present.
|
|
mipmapped = false;
|
|
buffer=(unsigned char *)calloc(4,rw * (rh+1));
|
|
deletebuffer=true;
|
|
//texheight=-h;
|
|
}
|
|
else
|
|
{
|
|
if (rw < w || rh < h)
|
|
{
|
|
// The texture is larger than what the hardware can handle so scale it down.
|
|
unsigned char * scaledbuffer=(unsigned char *)calloc(4,rw * (rh+1));
|
|
if (scaledbuffer)
|
|
{
|
|
Resize(w, h, rw, rh, buffer, scaledbuffer);
|
|
deletebuffer=true;
|
|
buffer=scaledbuffer;
|
|
}
|
|
}
|
|
}
|
|
// store the physical size.
|
|
|
|
int sourcetype;
|
|
if (glTextureBytes > 0)
|
|
{
|
|
if (glTextureBytes < 4) glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
|
static const int ITypes[] = { GL_R8, GL_RG8, GL_RGB8, GL_RGBA8 };
|
|
static const int STypes[] = { GL_RED, GL_RG, GL_BGR, GL_BGRA };
|
|
|
|
texformat = ITypes[glTextureBytes - 1];
|
|
sourcetype = STypes[glTextureBytes - 1];
|
|
}
|
|
else
|
|
{
|
|
sourcetype = GL_BGRA;
|
|
}
|
|
|
|
if (!firstCall && glBufferID > 0)
|
|
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, rw, rh, sourcetype, GL_UNSIGNED_BYTE, buffer);
|
|
else
|
|
glTexImage2D(GL_TEXTURE_2D, 0, texformat, rw, rh, 0, sourcetype, GL_UNSIGNED_BYTE, buffer);
|
|
|
|
if (deletebuffer && buffer) free(buffer);
|
|
else if (glBufferID)
|
|
{
|
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
|
|
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
|
}
|
|
|
|
if (mipmap && TexFilter[gl_texture_filter].mipmapping)
|
|
{
|
|
glGenerateMipmap(GL_TEXTURE_2D);
|
|
mipmapped = true;
|
|
}
|
|
|
|
if (texunit > 0) glActiveTexture(GL_TEXTURE0);
|
|
else if (texunit == -1) glBindTexture(GL_TEXTURE_2D, textureBinding);
|
|
return glTexID;
|
|
}
|
|
|
|
|
|
//===========================================================================
|
|
//
|
|
//
|
|
//
|
|
//===========================================================================
|
|
void FHardwareTexture::AllocateBuffer(int w, int h, int texelsize)
|
|
{
|
|
int rw = GetTexDimension(w);
|
|
int rh = GetTexDimension(h);
|
|
if (texelsize < 1 || texelsize > 4) texelsize = 4;
|
|
glTextureBytes = texelsize;
|
|
bufferpitch = w;
|
|
if (rw == w || rh == h)
|
|
{
|
|
glGenBuffers(1, &glBufferID);
|
|
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, glBufferID);
|
|
glBufferData(GL_PIXEL_UNPACK_BUFFER, w*h*texelsize, nullptr, GL_STREAM_DRAW);
|
|
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
|
|
}
|
|
}
|
|
|
|
|
|
uint8_t *FHardwareTexture::MapBuffer()
|
|
{
|
|
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, glBufferID);
|
|
return (uint8_t*)glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY);
|
|
}
|
|
|
|
//===========================================================================
|
|
//
|
|
// Destroys the texture
|
|
//
|
|
//===========================================================================
|
|
FHardwareTexture::~FHardwareTexture()
|
|
{
|
|
if (glTexID != 0) glDeleteTextures(1, &glTexID);
|
|
if (glBufferID != 0) glDeleteBuffers(1, &glBufferID);
|
|
}
|
|
|
|
|
|
//===========================================================================
|
|
//
|
|
// Binds this patch
|
|
//
|
|
//===========================================================================
|
|
unsigned int FHardwareTexture::Bind(int texunit, bool needmipmap)
|
|
{
|
|
if (glTexID != 0)
|
|
{
|
|
if (lastbound[texunit] == glTexID) return glTexID;
|
|
lastbound[texunit] = glTexID;
|
|
if (texunit != 0) glActiveTexture(GL_TEXTURE0 + texunit);
|
|
glBindTexture(GL_TEXTURE_2D, glTexID);
|
|
// Check if we need mipmaps on a texture that was creted without them.
|
|
if (needmipmap && !mipmapped && TexFilter[gl_texture_filter].mipmapping)
|
|
{
|
|
glGenerateMipmap(GL_TEXTURE_2D);
|
|
mipmapped = true;
|
|
}
|
|
if (texunit != 0) glActiveTexture(GL_TEXTURE0);
|
|
return glTexID;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
unsigned int FHardwareTexture::GetTextureHandle(int translation)
|
|
{
|
|
return glTexID;
|
|
}
|
|
|
|
void FHardwareTexture::Unbind(int texunit)
|
|
{
|
|
if (lastbound[texunit] != 0)
|
|
{
|
|
if (texunit != 0) glActiveTexture(GL_TEXTURE0+texunit);
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
if (texunit != 0) glActiveTexture(GL_TEXTURE0);
|
|
lastbound[texunit] = 0;
|
|
}
|
|
}
|
|
|
|
void FHardwareTexture::UnbindAll()
|
|
{
|
|
for(int texunit = 0; texunit < 16; texunit++)
|
|
{
|
|
Unbind(texunit);
|
|
}
|
|
gl_RenderState.ClearLastMaterial();
|
|
}
|
|
|
|
//===========================================================================
|
|
//
|
|
// Creates a depth buffer for this texture
|
|
//
|
|
//===========================================================================
|
|
|
|
int FHardwareTexture::GetDepthBuffer(int width, int height)
|
|
{
|
|
if (glDepthID == 0)
|
|
{
|
|
glGenRenderbuffers(1, &glDepthID);
|
|
glBindRenderbuffer(GL_RENDERBUFFER, glDepthID);
|
|
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8,
|
|
GetTexDimension(width), GetTexDimension(height));
|
|
glBindRenderbuffer(GL_RENDERBUFFER, 0);
|
|
}
|
|
return glDepthID;
|
|
}
|
|
|
|
|
|
//===========================================================================
|
|
//
|
|
// Binds this texture's surfaces to the current framrbuffer
|
|
//
|
|
//===========================================================================
|
|
|
|
void FHardwareTexture::BindToFrameBuffer(int width, int height)
|
|
{
|
|
width = GetTexDimension(width);
|
|
height = GetTexDimension(height);
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, glTexID, 0);
|
|
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, GetDepthBuffer(width, height));
|
|
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, GetDepthBuffer(width, height));
|
|
}
|
|
|
|
|
|
//===========================================================================
|
|
//
|
|
// Binds a texture to the renderer
|
|
//
|
|
//===========================================================================
|
|
|
|
bool FHardwareTexture::BindOrCreate(FTexture *tex, int texunit, int clampmode, int translation, int flags)
|
|
{
|
|
int usebright = false;
|
|
|
|
bool needmipmap = (clampmode <= CLAMP_XY);
|
|
|
|
// Bind it to the system.
|
|
if (!Bind(texunit, needmipmap))
|
|
{
|
|
|
|
int w = 0, h = 0;
|
|
|
|
// Create this texture
|
|
|
|
FTextureBuffer texbuffer;
|
|
|
|
if (!tex->isHardwareCanvas())
|
|
{
|
|
texbuffer = tex->CreateTexBuffer(translation, flags | CTF_ProcessData);
|
|
w = texbuffer.mWidth;
|
|
h = texbuffer.mHeight;
|
|
}
|
|
else
|
|
{
|
|
w = tex->GetTexelWidth();
|
|
h = tex->GetTexelHeight();
|
|
}
|
|
if (!CreateTexture(texbuffer.mBuffer, w, h, texunit, needmipmap, "FHardwareTexture.BindOrCreate"))
|
|
{
|
|
// could not create texture
|
|
return false;
|
|
}
|
|
}
|
|
if (tex->isHardwareCanvas()) static_cast<FCanvasTexture*>(tex)->NeedUpdate();
|
|
GLRenderer->mSamplerManager->Bind(texunit, clampmode, 255);
|
|
return true;
|
|
}
|
|
|
|
}
|